[[TableOfContents]] === ë¬¸ì œ ì œê¸°ë¥¼ ë‹´ì€ ì„¤ëª… 코드 === * ë‹¤ìŒ ì†ŒìŠ¤ì—서는 die메소드ì—ì„œ ìžì‹ ì„ ì‚ì œí•´ì¤ë‹ˆë‹¤. 그런ë°. ì‚ì œë˜ê³ ë„ ë»”ë»”ìŠ¤ëŸ½ê²Œ ìžì‹ ì˜ ë©”ì†Œë“œë¥¼ 호출하네요. 어떻게 ëœê²ƒì¼ê¹Œìš”? 알아봅시다. {{{~cpp #include <iostream> using namespace std; class Foo{ public: int id; Foo(){ static idSequance = 0; id = idSequance++; cout << "Create! id = " << id << endl; } void die(){ cout << "I suicide. Id is " << id << endl; delete this; // ìžì‹ ì‚ì œ } void sayMyId(){ cout << "My Id is no " << id << endl; } void sayHello(){ cout << "say Hello" << endl; } }; int main(int argc, char** argv) { cout << endl << ":::::: Case 1 - ë™ì í• ë‹¹ " << endl; Foo* foo1 = new Foo();// Create! 를 ì¶œë ¥í•œë‹¤. foo1->sayHello(); // Hello ì¶œë ¥ foo1->sayMyId(); // id ì¶œë ¥ foo1->die(); // ê°ì²´ ì‚ì œ foo1->sayHello(); // ë™ìž‘ 가능 foo1->sayMyId(); // ë™ìž‘ 가능 // id 확ì¸ì°¨ cout << endl << ":::::: Case 2 - id 확ì¸" << endl; Foo* foo2 = new Foo();// Create! 를 ì¶œë ¥í•œë‹¤. foo2->sayHello(); // Hello World ì¶œë ¥ foo2->sayMyId(); // id ì¶œë ¥ foo2->die(); foo2->sayHello(); foo2->sayMyId(); cout << endl << ":::::: Case 3 - í¬ì¸í„° NULLë¡œ í•˜ê³ ë©”ì†Œë“œ 호출하기 " << endl; Foo* foo3 = NULL; // ìžì„¸í•œ 설명보세요. foo3->sayHello(); // Hello 를 ì¶œë ¥í•œë‹¤. //foo3->sayMyId(); // debug, release ëª¨ë‘ ë™ìž‘í• ìˆ˜ 없다. //foo3->die(); // debug, release ëª¨ë‘ ë™ìž‘í• ìˆ˜ 없다. cout << endl << ":::::: Case3 - 지ì—변수로 ì„ ì–¸" << endl; Foo foo4; foo4.sayHello(); // Hello World ì¶œë ¥ foo4.sayMyId(); // id ì¶œë ¥ foo4.die(); // debug modeì—ì„œ assertion error foo4.sayHello(); // release ì—ì„œ ë™ìž‘ 가능 foo4.sayMyId(); // release ì—ì„œ ë™ìž‘ 가능 cout << endl; return 0; } }}} 결과는 다ìŒê³¼ ê°™ì´ ì¶œë ¥ë©ë‹ˆë‹¤. 비êµí•´ë³´ë©´ì„œ, ìƒê°í•´ ë³´ê³ ì´í•´ê°€ì§€ 않는다면 ìžì„¸í•œ ì„¤ëª…ì„ ë³´ì„¸ìš”. {{{~cpp :::::: Case 1 - ë™ì í• ë‹¹ Create! id = 0 say Hello My Id is no 0 I suicide. Id is 0 say Hello My Id is no 3604872 :::::: Case 2 - id í™•ì¸ Create! id = 1 say Hello My Id is no 1 I suicide. Id is 1 say Hello My Id is no 3604872 :::::: Case 3 - í¬ì¸í„° NULLë¡œ í•˜ê³ ë©”ì†Œë“œ 호출하기 say Hello :::::: Case3 - 지ì—변수로 ì„ ì–¸ Create! id = 2 say Hello My Id is no 2 I suicide. Id is 2 say Hello My Id is no 2 Press any key to continue }}} === ê°ê°ì˜ ê²½ìš°ì— ë”°ë¥¸ 테스트 === ==== Case1 - ë™ì í• ë‹¹ ==== {{{~cpp Foo* foo1 = new Foo(); // Create! 를 ì¶œë ¥í•œë‹¤. foo1->sayHello(); // Hello World ì¶œë ¥ foo1->sayMyId(); // id ì¶œë ¥ foo1->die(); // 죽였다. foo1->sayHello(); // í˜¸ì¶œì´ ëœë‹¤? foo1->sayMyId(); // ì´ê²ƒë„..--; }}} ==== Case2 - í¬ì¸í„° NULLë¡œ í•˜ê³ ë©”ì†Œë“œ 호출하기 ==== {{{~cpp Foo* foo2 = NULL; // ìžì„¸í•œ 설명보세요. foo2->sayHello(); // Hello 를 ì¶œë ¥í•œë‹¤.(ìŒ--;) foo2->sayMyId(); // debug, release ëª¨ë‘ ë™ìž‘í• ìˆ˜ 없다. foo2->die(); // debug, release ëª¨ë‘ ë™ìž‘í• ìˆ˜ 없다. }}} ==== Case3 - 지ì—변수로 ì„ ì–¸ ==== {{{~cpp Foo foo3; foo3.sayHello(); // Hello World ì¶œë ¥ foo3.sayMyId(); // id ì¶œë ¥ foo3.die(); // debug modeì—ì„œ assertion error foo3.sayHello(); // release ì—ì„œ ë™ìž‘ 가능 foo3.sayMyId(); // release ì—ì„œ ë™ìž‘ 가능 }}} DeleteMe ì´ë ‡ê²Œ ë³´ì´ëŠ” ê²ƒë„ ì¢‹ì§€ë§Œ, '''ì‹¤í–‰í• ìˆ˜ 있는 ì™„ì „í•œ 소스''' 형태를 ì œì‹œí•˜ëŠ” íŽ¸ì´ ë” ì¢‹ì„것 같습니다. --NeoCoin === ìžì„¸í•œ 설명 === ìœ„ì˜ "간단한 설명=코드"를 ì¸ìš©í•˜ë©´ì„œ 설명합니다. C++ì˜ ëª©í‘œëŠ” 기존 Cì˜ ì„±ëŠ¥ì„ í•´í•˜ì§€ 않으면서 OOP를 섞는 것입니다. í•„ì—°ì 으로 OOPì ì‚¬ê³ ì—ì„œ 용납하기 ì–´ë ¤ìš´ 코드를 ì‹¤í–‰í• ìˆ˜ 있습니다. OOP를 Cì˜ êµ¬í˜„ 위ì—ì„œ 해야 ë©ë‹ˆë‹¤. ì´ ì´ìƒí•˜ê²Œ ë³´ì´ëŠ” 코드를 ì´í•´í•˜ê¸° 위해서는 C++ì˜ ì›ë¡ ì ì¸ ë°°ê²½ì§€ì‹ì´ 필요합니다. * 첫째로, C++ì˜ í´ëž˜ìŠ¤ì™€ ì¸ìŠ¤í„´ìŠ¤ ìƒì„±ì„ 알아봅시다. * 둘째로, ì¸ìŠ¤í„´ìŠ¤ì— ê·€ì†ëœ 멤버 í•¨ìˆ˜ë“¤ì„ ì‹¤í–‰í•˜ëŠ” ê²ƒì„ ìƒê°í•´ ë³´ê² ìŠµë‹ˆë‹¤. * 마지막으로, ìœ„ì˜ ë¬¸ì œì œê¸°ì— ëŒ€í•´ 분ì„í•´ 봅시다. ==== C++ì—ì„œ í´ëž˜ìŠ¤ì˜ ì„ ì–¸ê³¼ ì¸ìŠ¤í„´ìŠ¤ì˜ ìƒì„±ìž…니다. ==== ìžì‹ ì´ ì»´íŒŒì¼ëŸ¬ê°€ ë˜ì—ˆë‹¤ê³ ê°€ì •í•´ 봅시다. 우리가 class를 ì„ ì–¸í•˜ê³ ì»´íŒŒì¼í•˜ë ¤ë©´ í”„ë¡œê·¸ëž¨ì˜ ì˜ì—ì— class ì˜ Data ë“¤ì„ ì €ìž¥í• ìˆ˜ 있는 "class í‹€"ì˜ ì •ë³´ë¥¼ ë‹´ì•„ ë†“ì„ ê³³ì´ í•„ìš”í•©ë‹ˆë‹¤. {{{~cpp class Foo{ public: int id; // <- ì´ ë¶€ë¶„ì˜ ì •ë³´ 입니다. }; }}} C++ì€ Strong typed language ì´ë¯€ë¡œ ì»´íŒŒì¼ ì‹œê°„ì— ëª¨ë“ í˜•ì´ ì„ ì–¸ë˜ê³ , 사용ë˜ëŠ” ê²ƒì„ ê²€ì¦í• 수 있습니다. 하지만 ì´ëŠ” 다ìŒê³¼ ê°™ì€ ì§€ì— ë³€ìˆ˜ì— êµí•œí•©ë‹ˆë‹¤. {{{~cpp Foo foo4; }}} new 키워드로 í• ë‹¹ì‹œì—는 runtime ì— class ì˜ instance 를 ì°ì–´ 낼수 있어야 합니다. ì´ë¥¼ 위해 프로그램 안ì—는 ìœ„ì˜ idê°€ int ë¼ëŠ” ì •ë³´ë¥¼ 담는 classì˜ "class í‹€" ì •ë³´ë¥¼ 담는 ê³³ì´ í•„ìš”í•©ë‹ˆë‹¤. 여기까지가, class 와 struct 키워드가 하는 ë™ì¼í•œ 작업입니다. ê·¸ë¦¬ê³ , class ì—는 몇가지 ë” ìƒê°í•´ì•¼ 하는ë°, 그중 하나가 foo 를 ì´ìš©í•´ì„œ ì–´ë– í•œ member 함수를 í˜¸ì¶œí• ìˆ˜ 있는가 입니다. 그러나, 컴파ì¼ëŸ¬ì¸ 우리는 ì´ ì •ë³´ë¥¼ ì§€ì— ë³€ìˆ˜ì´ë“ , new ë¡œ í• ë‹¹ì´ë“ ì»´íŒŒì¼ ì‹œê°„ì— ì¸ìžì˜ typeì„ ë³´ê³ í•¨ìˆ˜ 호출 ìœ íš¨ì„±ì„ í™•ì¸í•˜ê³ , ì ì ˆí•œ 함수 í¬ì¸í„°ë¥¼ 함수 호출하는 ë¶€ë¶„ì— ë„£ì–´ 줄수 있습니다. ê·¸ë¦¬ê³ ì‹¤í–‰í• ìˆ˜ ìžˆëŠ”ë° ì´ ê³¼ì •ì„ ë‘번째ì—ì„œ 설명합니다. 그외 class와 instanceì˜ ìƒì„±ì‹œ vpt와, ìƒì† ê´€ê³„ì— ëŒ€í•œ pointer ì •ë³´ê°€ ë” ë“¤ì–´ 가야 합니다. 그러나 여기ì—서는 ìƒê°í•˜ì§€ 않습니다. 둘째로 넘어갑니다 ==== instance ì— ê·€ì†ëœ 멤버 í•¨ìˆ˜ë“¤ì„ ì‹¤í–‰í•´ 봅시다. ==== ìž ê³„ì† ìš°ë¦¬ëŠ” 컴파ì¼ëŸ¬ 입니다. 컴파ì¼ëŸ¬ì¸ 우리는 member 함수 호출 ë¶€ë¶„ì„ í•¨ìˆ˜ì˜ ì‹¤í–‰ì½”ë“œë¥¼ 가리키는 함수 í¬ì¸í„°ë¡œ ëª¨ë‘ êµì²´í•˜ì˜€ìŠµë‹ˆë‹¤. ê·¸ë¦¬ê³ ì¸ê°„으로 ëŒì•„옵시다. C++ 표준안ì—ì„œ ì „ì—ì—ì„œ 함수 호출과, instanceì— ê·€ì†ëœ 멤버 í•¨ìˆ˜ë“¤ì˜ í˜¸ì¶œì„ ê°€ë¦¬ì§€ 않습니다. 함수 ì„ ì–¸ê³¼ 멤버 함수 ì„ ì–¸ì˜ í•¨ìˆ˜ 실행 코드는 ëª¨ë‘ ë™ì¼ 방법으로 ì„ ì–¸ë˜ê³ , ëª¨ë‘ ë™ì¼í•œ ë©”ì»¤ë‹ˆì¦˜ì˜ í•¨ìˆ˜ í¬ì¸í„°ë¥¼ ì´ìš©í•´ì„œ 호출합니다. ì´ëŸ¬í•œ ì „ì œë¼ë©´, 한가지 ì˜ë¬¸ì´ ìƒê¹ë‹ˆë‹¤. '''ì „ì— í•¨ìˆ˜ì™€ ë™ì¼í•œ 함수 ì„ ì–¸ì˜ í˜•íƒœë¼ë©´ ê°ê°ì˜ instanceì— ì–´ë–»ê²Œ ì ‘ê·¼í•˜ëŠ”ê°€?''' 무슨 ì†Œë¦¬ëƒ í•˜ë©´, {{{~cpp class Foo{ public: int id; void sayMyId(){ cout << "My Id is no " << id << endl; } }; }}} ë¼ëŠ” 함수는 ê° instanceì˜ id ë¼ëŠ” ì¸ìžì— ì ‘ê·¼í•©ë‹ˆë‹¤. 그러나 {{{~cpp Foo::sayMyId()}}} ê°™ì€ ì•„ë¬´ëŸ° ì¸ìž 없는 í•¨ìˆ˜ì˜ ì‹¤í–‰ì½”ë“œê°€ 함수 ì„ ì–¸ ì˜ì—ì— ì„¸íŒ…ëœë‹¤ë©´, id ë¼ëŠ” ì¸ìžì— ì ‘ê·¼í• ìˆ˜ 없습니다. C++ ì—서는 ì´ëŸ° 한계를 class ì— ê·€ì†ëœ í•¨ìˆ˜ë“¤ì˜ ì²˜ìŒ ì¸ìžë¡œ 해당 class ì˜ í¬ì¸í„°ë¥¼ 묵시ì 으로 ì„ ì–¸í•´ì„œ í•´ê²°í•˜ê³ ìžˆìŠµë‹ˆë‹¤. 즉, {{{~cpp Foo:sayMyId() }}} ë¼ëŠ” 함수 실행 코드가 함수 ì˜ì—ì— ì„ ì–¸ë ë•Œ 컴파ì¼ëŸ¬ê°€ {{{~cpp Foo:sayMyId(Foo* x) }}} ë¼ëŠ” í˜•íƒœì˜ í•¨ìˆ˜ë¡œ ì„ ì–¸í•˜ê³ , ì‹¤í–‰í• ìˆ˜ 있ë„ë¡ ë§Œë“니다. ê·¸ë¦¬ê³ , 호출한다면 {{{~cpp Foo*}}} ë¶€ë¶„ì— {{{~cpp foo1->sayMyId(foo1); }}} 형태로 함수 í¬ì¸í„°ë¥¼ 세팅해서 함수 코드를 실행합니다. C++ì€ ì´ëŸ¬í•œ 다소 황당한 수로 Cì™€ì˜ ì»´íŒŒì¼ì‹œ í˜¸í™˜ì„±ì„ ì§€í‚¤ë©´ì„œ OOP를 구현합니다. 사족. ì´ëŸ¬í•œ ì‚¬ì—°ì´ classë‚´ì—ì„œ static 멤버 함수를 ì„ ì–¸í•˜ê³ instanceì—ì„œ í˜¸ì¶œí• ë•Œ instance ì˜ ë©¤ë²„ ë³€ìˆ˜ì— ì ‘ê·¼í•˜ì§€ 못하는 ì´ìœ ê°€ ë©ë‹ˆë‹¤. static 함수로 ì„ ì–¸ 하면 묵시ì 으로 pointer 를 세팅하지 ì•Šê³ í•¨ìˆ˜ë¥¼ 호출합니다. ì´ëŸ° ë°°ê²½ 지ì‹ì„ 바탕으로 다ìŒìœ¼ë¡œ 넘어갑니다. ==== ìœ„ì˜ ë¬¸ì œì œê¸°ì— ëŒ€í•´ 분ì„í•´ 봅시다. ==== 다ìŒê³¼ ê°™ì€ ì½”ë“œë¥¼ 실행하면, {{{~cpp Foo* foo1 = new Foo(); // Create! 를 ì¶œë ¥í•œë‹¤. foo1->sayHello(); // Hello ì¶œë ¥ foo1->sayMyId(); // id ì¶œë ¥ foo1->die(); // ê°ì²´ ì‚ì œ foo1->sayHello(); // ë™ìž‘ 가능 foo1->sayMyId(); // ë™ìž‘ 가능 }}} ì´ë ‡ê²Œ 나옵니다.(윈ë„우즈 플랫í¼ì—ì„œ) {{{~cpp Create! id = 0 say Hello My Id is no 0 I suicide. Id is 0 say Hello // ê°ì²´ ì‚ì œ(delete this)후 실행 코드 My Id is no 3604872 // ê°ì²´ ì‚ì œ(delete this)후 실행 코드 }}} ì´ë ‡ê²Œ 나옵니다. (C++ ì£¼ì„ ë¹¼ê³ ) 위ì—ì„œ ë¬¸ì œì‹œ ë˜ëŠ” 부분ì€, í›„ë°˜ì˜ ë‘가지 {{{~cpp sayHello() 와 sayMyId()}}} ì¼ê²ë‹ˆë‹¤. 둘째 ì„¤ëª…ì˜ member 함수를 호출하는 ë©”ì»¤ë‹ˆì¦˜ì„ ì´í•´í–ˆë‹¤ë©´ {{{~cpp sayHello() -> sayHello(Foo*) sayMyId() -> sayMyId(Foo*) }}} 형태로 호출ëœë‹¤ëŠ” ê²ƒì„ ì§ìž‘í• ìˆ˜ ìžˆì„ ê²ë‹ˆë‹¤. 하지만 ë‘ í•¨ìˆ˜ëŠ” 다른 ì ì´ ìžˆìŠµë‹ˆë‹¤. sayHello()는 instance variableì— ì ‘ê·¼í•˜ì§€ 않는다는 것ì´ê³ , {{{~cpp sayMyId()}}} 는 ì ‘ê·¼í•œë‹¤ëŠ” ì ì´ì§€ìš”. ë”°ë¼ì„œ, ì‚ì œí›„ì— ë©”ëª¨ë¦¬ë¥¼ ì²ì†Œ(?) í•´ë²„ë¦¬ê³ ë‚œí›„ 해당 instance ë¶€ë¶„ì„ ì‹¤í–‰í•´ 보리니 idê°€ 다ìŒê³¼ ê°™ì´ ì—‰ëš±í•˜ê²Œ 나오지요. {{{~cpp say Hello ê°ì²´ ì‚ì œ(delete this)후 실행 코드 My Id is no 3604872 ê°ì²´ ì‚ì œ(delete this)후 실행 코드 }}} instanceì— ì‚¬ìš©ë˜ì—ˆë˜ 메모리는, 해당 processì˜ ê°€ìš© 메모리로 ëŒì•„가지, ì ‘ê·¼ 금지 ì˜ì—으로 세팅ë˜ì§€ 않습니다. ì´ ë¶€ë¶„ì€ delete this ì‹œ 해당 instance ì˜ì—ì˜ ê°’ì„ ì–´ë–»ê²Œ "ì²ì†Œ"하ëŠëƒì— ë”°ë¼ì„œ, í”Œëž«í¼ ë³„ë¡œ 다르게 나옵니다. ê·¸ë¦¬ê³ {{{~cpp delete x }}} ë¼ëŠ” 코드는 xì˜ ê°’ì„ ë³€í™”ì‹œí‚¤ì§€ 않습니다. 변화시킬수 없습니다. ì´ìœ 는 call by value ë¡œ 넘어온 xì˜ ê°’ì„ NULLë¡œ 변경시켜봤ìž, ì˜í–¥ 받지 않는 코드가 경우가 있기 때문ì—, 변화시킬 í•„ìš”ì„±ì´ ì—†ìŠµë‹ˆë‹¤. 만약 구현 컴파ì¼ëŸ¬ê°€, 메모리를 system으로 ë°˜í™˜í•˜ê³ ì ‘ê·¼ 금지 ì˜ì—으로 ì„¤ì •í•œë‹¤ë©´, ë‹¤ìŒ ê°•ì œ ì ‘ê·¼ì‹œ access ì—러가 ë‚˜ê² ì§€ìš”. ì´ëŸ¬í•œ 비효율ì 구현 í”Œëž«í¼ ì—†ì„ ê²ë‹ˆë‹¤. foo2~3 ì´ëŸ¬í•œ ë°°ê²½ 지ì‹ì„ ì´ìš©í•´ì„œ 결과를 ì´í•´í• 수 있는 보충 설명입니다. ê·¸ 중 foo2는 C++ì—ì„œ 볼수 있는 웃긴 ì˜ˆì œì§€ìš”. (foo3ë„ ì›ƒê¸°ì§€ë§Œ) {{{~cpp Foo* foo2 = NULL; // ìžì„¸í•œ 설명보세요. foo2->sayHello(); // Hello 를 ì¶œë ¥í•œë‹¤. }}} ì´ ì½”ë“œì˜ ê²°ê³¼ëŠ” {{{~cpp say Hello }}} ê³¼ ê°™ì´ ìž˜ ë™ìž‘합니다. 호출 형태가 {{{~cpp Foo::sayHello(foo2) -> Foo::sayHello(NULL) }}} ì´ ëœê²ƒì´ê³ , sayHello ë‚´ì—는 pointer ê°’ NULL ì„ ì´ìš©í•œ ì ‘ê·¼ì´ ì—†ìœ¼ë¯€ë¡œ 아무런 ì—러가 ë°œìƒí•˜ì§€ 않습니다. OOPì ì¸ ì‚¬ê³ ë¡œ ì‹¤í–‰í• instanceê°€ 없으므로 불가능한 코드지만, 잘 실행ë©ë‹ˆë‹¤. C++ì—ì„œ 함수 실행시 해당 instanceì˜ ì¡´ìž¬ ìœ ë¬´ë¥¼ 검사하지 않습니다. 검사 í• ìˆ˜ë„ ì—†ê² ì§€ìš”. NULL ì¡°ì°¨ 0 ì´ë¼ëŠ” pointer ê°’ì— í•´ë‹¹í•˜ë‹ˆê¹Œìš”. === ì •ë¦¬ === * C++ ì—ì„œ class ì˜ ë©¤ë²„ 함수를 í˜¸ì¶œí• ë•Œ 멤버 í•¨ìˆ˜ì˜ ì²«ì¸ìžë¥¼ 해당 class ì˜ instance pointer ë¡œ 묵시ì 으로 ì„ ì–¸ë˜ê³ 호출ëœë‹¤. * ìœ„ì˜ í˜¸ì¶œì‹œì— pointer ì˜ ìœ íš¨ì„±ì€ í™•ì¸í•˜ì§€ 않는다. === ë§ë¶™ì—¬ Javaì—ì„œ 비슷한 코드를 봅시다. === instance ìœ ë¬´ë¥¼ 검사하는 Java 코드를 봅시다. {{{~cpp // Main.java file class Foo{ public int a; void sayHello(){ System.out.println("Hello"); } } public class Main { public static void main(String[] args) { Foo foo= null; foo.sayHello(); } } }}} ì´ë¥¼ 실행하면, 다ìŒê³¼ ê°™ì€ exceptionì„ ì¶œë ¥í•©ë‹ˆë‹¤. ì´ëŠ” [http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html Java Language Specification 2nd] (3rdê°€ ì•„ì§ ì•ˆë‚˜ì™”êµ°ìš”.) 와 [http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html jvm specification]ì„ ì°¸ê³ í•˜ì„¸ìš”. (실행시ì ì— null ê°’ì¸ì§€ ê²€ì‚¬í•˜ê³ , 필요시 instance poolì—ì„œ instance를 pointer를 ì´ìš©í•´ì„œ ì ‘ê·¼í•˜ëŠ” 것으로 기억합니다. ) {{{~cpp java.lang.NullPointerException at Main.main(Main.java:19) Exception in thread "main" }}} C++ì—서는 NULL ì´ í‚¤ì›Œë“œë¡œ 존재하지 ì•Šê³ , 0 ì´ë¼ê³ 확실히 약ì†ë˜ì–´ 있지 않기 ë•Œë¬¸ì— ê²€ì‚¬ìžì²´ê°€ 불가능합니다.그러나 vmìƒì˜ ì–¸ì–´ë“¤ì€ ê·¸ëž˜ì„œ ëª¨ë‘ nullì— í•´ë‹¹í•˜ëŠ” 키워드가 ì¡´ìž¬í•˜ê³ ê²€ì‚¬ê°€ 가능합니다. ---- [컬럼분류]