C #에서 if / else와 switch-case를 사용하는 것 사이에 중요한 차이점이 있습니까?


219

사용하는 장점 / 단점은 무엇입니까 switch대 문을 if/elseC #에서이. 코드의 모양 이외의 다른 점이 있다고 상상할 수 없습니다.

결과 IL 또는 관련 런타임 성능이 근본적으로 다른 이유가 있습니까?

관련 : 문자열을 켜거나 다른 유형을 입력하는 것이 더 빠릅니까?



3
이 질문은 자주 10 억 번 반복하는 경우가 아니라면 대부분의 개발자에게 이론적으로 흥미 롭습니다. (그런 다음 switch 문을 사용하여 48 초에서 43 초로 가십시오 ...) 또는 Donald Knuth의 말에서 : "우리는 작은 효율성을 잊어야합니다. 시간의 약 97 %를 말하십시오. 조기 최적화는 모든 악의 근원입니다" en.wikipedia.org/wiki/Program_optimization#When_to_optimize
Sire

스위치의 공유 범위가 좁기 때문에 스위치 대신 if / else를 자주 사용합니다.
Josh

답변:


341

SWITCH 문은 디버그 또는 호환성 모드의 IF와 동일한 어셈블리 만 생성합니다. 릴리스에서는 O (1) 인 MSIL 'switch'문을 통해 점프 테이블로 컴파일됩니다.

C # (다른 많은 언어와 달리)은 문자열 상수를 켤 수 있으며 약간 다르게 작동합니다. 임의 길이의 문자열에 대한 점프 테이블을 작성하는 것은 실용적이지 않으므로 대부분의 경우 이러한 스위치는 IF 스택으로 컴파일됩니다.

그러나 오버 헤드를 처리 할 수있는 조건 수가 충분하면 C # 컴파일러는 HashTable 개체를 만들고 문자열 상수로 채우고 해당 테이블을 조회 한 다음 점프합니다. 해시 테이블 조회는 엄격하게 O (1)가 아니며 눈에 띄는 일정한 비용이 있지만 대소 문자 레이블 수가 많으면 IF의 각 문자열 상수와 비교할 때보 다 훨씬 빠릅니다.

요약하면 조건 수가 5 이상인 경우 IF보다 SWITCH를 선호하고 그렇지 않으면 더 나은 것을 사용하십시오.


1
C # 컴파일러가 해시 테이블을 생성 할 것입니까? 위의 주석 토론에서 해시 테이블에 대해 한 요점은 C # 컴파일러가 아니라 기본 컴파일러에 관한 것입니다. C # 컴파일러는 해시 테이블을 생성하기 위해 어떤 임계 값을 사용합니까?
Scott Wisniewski

8
내가 생각하는 열명 정도. 안전한쪽에 있어야합니다. 그리고 btw, 내 분노는 당신이 아니라 사람들을 찬성하고 수용하는 사람들입니다.
ima

48
약간의 실험은 카운트 <= 6 : "if"; 카운트> = 7 : 사전. MS .NET 3.5 C # 컴파일러에서도 마찬가지입니다. 물론 버전과 공급 업체간에 변경 될 수 있습니다.
Jon Skeet

37
이봐, 사과 할게 뼈 머리가되어 죄송합니다.
Scott Wisniewski

실제 응용 분야의 후속 조치로서 대부분의 실제 차이가 있습니까? C #에서 스위치 문이 이상하다는 것을 알았습니다. 구문은 다른 것과 다르고 코드를 읽기 어렵게 만들거나 스위치 문을 사용하는 것이 귀찮은 지 또는 다른 경우에만 프로그래밍해야합니다. 성능 병목 현상이 발생하면 다시 교체하고 교체합니까?
Jason Masters

54

일반적으로 (모든 언어와 모든 컴파일러를 고려하면) 스위치 문은 컴파일러가 스위치 문에서 점프 테이블을 생성하기 쉽기 때문에 if / else 문보다 더 효율적일 수 있습니다. 적절한 제약 조건이 주어지면 if / else 문에 대해 동일한 작업을 수행 할 수는 있지만 훨씬 더 어렵습니다.

C #의 경우에도 마찬가지이지만 다른 이유로도 마찬가지입니다.

많은 수의 문자열을 사용하면 컴파일러에서 해시 테이블을 사용하여 점프를 구현하므로 switch 문을 사용하면 성능이 크게 향상됩니다.

적은 수의 문자열을 사용하면 둘 사이의 성능이 동일합니다.

이 경우 C # 컴파일러는 점프 테이블을 생성하지 않기 때문입니다. 대신 IF / ELSE 블록과 같은 MSIL을 생성합니다.

jitted가 점프 테이블을 사용하여 스위치 명령문을 구현하는 "스위치 명령문"MSIL 명령이 있습니다. 그러나 정수 유형에서만 작동합니다 (이 질문은 문자열에 대해 묻습니다).

적은 수의 문자열의 경우 컴파일러가 IF / ELSE 블록을 생성하는 것이 더 효율적이며 해시 테이블을 사용하는 것입니다.

처음에 이것을 알았을 때 IF / ELSE 블록이 적은 수의 문자열과 함께 사용 되었기 때문에 컴파일러는 많은 수의 문자열에 대해 동일한 변환을 수행했다고 가정했습니다.

이것은 잘못되었습니다. 'IMA'는 이것을 나에게 지적하기에 충분히 친절했습니다 (음. 그는 친절하지 않았지만 그는 옳았습니다.

또한 MSIL에 "스위치"명령이 없다는 것에 대한 본론을 세웠습니다 (스위치 기본 요소가있는 경우 해시 테이블과 함께 사용하지 않은 이유를 알았으므로 스위치 기본 요소가 없어야합니다. ...). 이것은 모두 틀렸고, 엄청나게 바보였습니다. 다시 'IMA'가 이것을 지적했습니다.

가장 높은 등급의 게시물이기 때문에 여기에서 업데이트했으며 허용되는 답변입니다.

그러나 REP가 잘못되었다고 생각하지 않기 때문에 Community Wiki로 만들었습니다. 기회가 생기면 투표 'ima'의 게시물을 올리십시오.


3
MSIL에는 스위치 프리미티브가 있으며 c # 문은 일반적으로 C와 같은 조회로 컴파일됩니다. 특정 상황 (대상 플랫폼, cl 스위치 등)에서 컴파일하는 동안 스위치가 IF로 확장 될 수는 있지만 대체 호환성 측정 방법 일뿐입니다.
ima

6
내가 할 수있는 것은 바보 같은 실수를 한 것에 대해 사과하는 것입니다. 나를 믿어, 나는 그것에 대해 바보 느낌. 진심으로, 나는 그것이 여전히 가장 좋은 대답이라고 생각합니다. 네이티브 컴파일러에서는 해시 테이블을 사용하여 점프를 구현할 수 있으므로 끔찍한 것은 아닙니다. 나는 하나의 실수를했다.
Scott Wisniewski

9
ima, 오류가 있으면 지적하십시오. 스캇과 같은 소리로 게시물을 수정 해 드리겠습니다. 그렇지 않은 경우, 답변을 수정하는 능력을 얻은 다른 사람들도 그렇게 할 것입니다. 이것이 이와 같은 사이트가 작동하는 유일한 방법이며 일반적으로 작동하는 것 같습니다. 아니면 당신의 공을 가지고 집으로 이동 :)
jwalkerjr

2
@Scott : 두 번째와 세 번째 단락을 편집하여 "문자열에 대해"명시 적으로 언급하는 것이 좋습니다. 하단의 업데이트를 읽지 못할 수도 있습니다.
존 스키트

4
@ima 답이 객관적으로 잘못되었다고 생각되면 정답을 편집하십시오. 그래서 모든 사람이 답변을 편집 할 수 있습니다.
Miles Rout

18

선호하는 세 가지 이유 switch:

  • 네이티브 코드를 대상으로하는 컴파일러는 종종 switch 문을 하나의 조건부 분기와 간접 점프 로 컴파일 할 수 있지만 ifs 시퀀스 에는 일련의 조건부 분기 가 필요합니다 . 사례의 밀도에 따라 사례 진술을 효율적으로 컴파일하는 방법에 대해 많은 학습 논문이 작성되었습니다. 일부는 lcc 컴파일러 페이지 에서 링크됩니다 . (Lcc는 스위치를위한 더 혁신적인 컴파일러 중 하나를 가졌습니다.)

  • switch 문은 상호 배타적 인 대안 중에서 선택 되며 스위치 구문 은이 제어 흐름을 프로그래머에게보다 투명하게 만들고 if-then-else 문의 중첩을 만듭니다.

  • ML 및 Haskell을 포함한 일부 언어에서는 컴파일러가 사례를 생략했는지 확인합니다 . 이 기능을 ML 및 Haskell의 주요 장점 중 하나로 생각합니다. C #이 이것을 할 수 있는지 모르겠습니다.

일화 : 평생 공로상을 수상한 강연에서 Tony Hoare가 자신의 경력에서 한 모든 일에 대해 가장 자랑스럽게 생각하는 세 가지가 있다고 들었습니다.

  • 퀵소트 발명
  • 스위치 문 발명 (토니가 case 명령문을 )
  • 업계에서 경력을 시작하고 끝내기

나는 없이 사는 상상할 수 없습니다switch .


16

컴파일러는 약간의 차이 (Knuth, anyone?)를 사용하여 거의 모든 것을 동일한 코드로 최적화 할 것입니다.

차이점은 switch 문이 다른 문을 함께 묶으면 15 개보다 깨끗하다는 것입니다.

친구는 친구가 if-else 문을 쌓아 두지 못하게합니다.


13
"친구들은 친구들이 if-else 문장을 쌓을 수 없습니다." 당신은 bumber의 sitcker :)해야
매튜 M. 오스본

14

실제로 switch 문이 더 효율적입니다. 컴파일러는 if / else 문으로는 찾을 수없는 조회 테이블에 최적화합니다. 단점은 switch 문을 변수 값과 함께 사용할 수 없다는 것입니다.
당신은 할 수 없습니다 :

switch(variable)
{
   case someVariable
   break;
   default:
   break;
}

그건 그래야만 해

switch(variable)
{
  case CONSTANT_VALUE;
  break;
  default:
  break;
}

1
당신은 어떤 종류의 숫자가 있습니까? 컴파일러가 If / Else에 대해 swtich 문을 얼마나 잘 최적화 할 수 있는지 궁금합니다.
Matthew M. Osborn

예 나는 스위치 문이 항상 O (1)로 최적화된다고 생각합니다. 여기서 else 문은 O (n)입니다. 여기서 n은 if / else if 문의 올바른 값의 위치입니다.
kemiller2002

C #의 경우에는 사실이 아닙니다. 자세한 내용은 아래 내 게시물을 참조하십시오.
Scott Wisniewski

나는 그것에 대해 완전히 확신하지는 못했지만 내가 찾은 맹세 한 책에서 정보를 찾을 수 없습니다. 최적화없이 MSIL 코드를보고 있지 않습니까? 최적화를 사용하여 컴파일하지 않으면 점프 테이블이 생성되지 않습니다.
kemiller2002

디버그 및 소매 모드로 컴파일했으며 두 경우 모두 if / else 블록을 생성합니다. 보고있는 책이 C #에 대해 이야기하고 있었습니까? 이 책은 아마도 컴파일러 책이거나 C 또는 C ++에 관한 책일 것입니다
Scott Wisniewski

14

나는 다른 사람이 스위치 진술의 가정 된 효율성 이점이 거의 똑같이 가능성이있는 다양한 경우에 달려 있다고 (명백한?) 지점을 제기하는 것을 보지 못했습니다. 값 중 하나 (또는 ​​몇 개)가 훨씬 가능성이 높은 경우 가장 일반적인 경우를 먼저 확인하여 if-then-else 래더가 훨씬 빠릅니다.

예를 들어,

if (x==0) then {
  // do one thing
} else if (x==1) {
  // do the other thing
} else if (x==2) {
  // do the third thing
}

vs

switch(x) {
  case 0: 
         // do one thing
         break;
  case 1: 
         // do the other thing
         break;
  case 2: 
         // do the third thing
         break;
}

x가 시간의 90 % 인 경우 "if-else"코드는 스위치 기반 코드보다 두 배 빠릅니다. 컴파일러가 "스위치"를 일종의 영리한 테이블 기반 goto로 바꾸어도 단순히 0을 확인하는 것만 큼 빠르지는 않습니다.


3
조기 최적화가 없습니다! 일반적으로 사례가 몇 개 이상이고 switch호환 switch가능 하면 문장이 더 좋습니다 (가독성 이 높고 때로는 더 빠름). 당신이 알고있는 경우 하나의 경우가 훨씬 더 가능성이 있다고, 당신은 형성이를 뽑을 수 if- else- switch구조를하고 더 빨리 잴 수 있다면 , 당신은에있는 것을두고 (반복, 필요한 경우.) IMO이 여전히 합리적으로 읽을 수.. 는 IF switch퇴화와이 너무 작 도착하는로 변환하는 작업의 대부분을 할 것입니다 정규식 - 대체 else if쇄를.
아무도

6
원래 질문 (3 년 전!)은 if / else와 switch 사이의 장점과 단점을 요구했습니다. 이것은 하나의 예입니다. 나는 개인적으로 이러한 종류의 최적화가 루틴의 런타임에 큰 차이를 만드는 것을 보았습니다.
Mark Bessey

7

종종 더 좋아 보일 것입니다. 즉, 진행 상황을 이해하기가 더 쉬울 것입니다. 성능상의 이점이 최소한으로 최소화 될 것이라는 점을 고려하면 코드보기가 가장 중요한 차이점입니다.

따라서 if / else가 더 좋아 보인다면 사용하고 그렇지 않으면 switch 문을 사용하십시오.


4

부가 주제이지만, 나는 종종 걱정하고 (더 자주 볼 수 있음) if/ elseswitch 너무 많은 경우에 진술이 너무 커집니다. 이들은 종종 유지 보수성을 손상시킵니다.

일반적인 범인은 다음과 같습니다.

  1. 여러 if 문에서 너무 많이 수행
  2. 인간적으로 분석 할 수있는 것보다 더 많은 사례 진술
  3. if 평가에서 찾을 대상을 알기에는 너무 많은 조건

고치다:

  1. 메소드 리팩토링으로 추출하십시오.
  2. 케이스 대신 메소드 포인터가있는 사전을 사용하거나 추가 구성 성을 위해 IoC를 사용하십시오. 분석법 공장도 도움이 될 수 있습니다.
  3. 자신의 방법으로 조건을 추출

4

이 링크에 따라 스위치와 if 문을 사용한 반복 테스트의 IF와 스위치 비교는 1,000,000,000 반복, 스위치 문 = 43.0sIf 문 = 48.0 초와 같습니다.

문자 그대로 초당 20833333 반복이므로 더 집중해야할까요?

추신 : 작은 조건 목록의 성능 차이를 알아야합니다.


그것은 나를 위해 그것을 못 박는 다.
Greg Gum

3

if 또는 else 문을 사용하는 경우 기본 솔루션이 비교? 운영자

(value == value1) ? (type1)do this : (type1)or do this;

스위치에서 또는 루틴을 수행 할 수 있습니다

switch(typeCode)
{
   case TypeCode:Int32:
   case TypeCode.Int64:
     //dosomething here
     break;
   default: return;
}

2

이것은 실제로 귀하의 질문에 대답하지는 않지만 컴파일 된 버전 사이에는 거의 차이가 없으므로 의도를 가장 잘 설명하는 방식으로 코드를 작성하는 것이 좋습니다. 컴파일러가 예상 한 작업을 수행 할 가능성이 높을뿐만 아니라 다른 사람이 코드를보다 쉽게 ​​유지 관리 할 수 ​​있습니다.

하나의 변수 / 속성 값을 기반으로 프로그램을 분기하려는 의도가 있다면 switch 문은 그 의도를 가장 잘 나타냅니다.

다른 변수 / 속성 / 조건에 따라 프로그램을 분기하려는 의도라면 if / else if 체인이 그 의도를 가장 잘 나타냅니다.

나는 코디가 break 명령을 잊어 버린 사람들에 대해 옳다는 것을 인정할 것이지만, {}가 잘못되는 블록이 조건문에 있어야하는 행이 잘못되면 사람들이 복잡 해지는 것을 자주 볼 수있다. 한 줄이 있어도 항상 if 문에 {}를 포함 시키는 이유 중 하나입니다 . 읽는 것이 더 쉬울뿐만 아니라 조건부에서 다른 줄을 추가해야하는 경우 추가하는 것을 잊을 수 없습니다.


2

관심 질문. 이것은 몇 주 전에 직장에서 나타 났으며 예제 스 니펫을 작성하고 .NET Reflector에서 보는 것으로 답을 찾았습니다 (반사판은 굉장합니다 !! 나는 그것을 좋아합니다).

이것이 우리가 발견 한 것입니다. 문자열 이외의 다른 유효한 스위치 문은 스위치 문으로 IL에 컴파일됩니다. 그러나 문자열 인 경우 IL에서 if / else if / else로 다시 작성됩니다. 따라서 우리의 경우 switch 문이 문자열을 비교하는 방법, 예를 들어 대 / 소문자를 구분하는 방법을 알고 싶었고 리플렉터는 신속하게 답변을주었습니다. 이것은 알기에 유용했습니다.

문자열을 대 / 소문자를 구분하여 비교하려면 String을 수행하는 것보다 빠르기 때문에 switch 문을 사용할 있습니다 .if / else에서 비교하십시오. (편집 : 실제 성능 테스트의 경우 빠른 속도, 문자열 켜기 또는 다른 유형 켜기?

switch (myString.ToLower())
{
  // not a good solution
}

가장 좋은 방법은 다음과 같이 스위치 구문이 의미가있는 경우 사용하는 것입니다.

  • 코드의 가독성을 향상시킵니다.
  • 값 범위 (float, int) 또는 열거 형을 비교하고 있습니다.

switch 문에 공급할 값을 조작해야하는 경우 (전환 할 임시 변수 작성) if / else 제어문을 사용해야합니다.

업데이트:

문자열을 대문자 (예 :)로 변환하는 것이 좋습니다. 예를 들어 ToUpper()Just-In-Time 컴파일러가와 비교할 때 수행 할 수있는 최적화가 추가 되었기 때문 ToLower()입니다. 그것은 미세 최적화이지만, 긴밀한 루프에서는 유용 할 수 있습니다.


약간의 참고 사항 :

switch 문의 가독성을 높이려면 다음을 시도하십시오.

  • 가장 가능성이 높은 지점을 먼저 배치하십시오.
  • 모두 발생하기 쉬운 경우 알파벳 순서로 나열하여 쉽게 찾을 수 있도록하십시오.
  • 마지막 남은 조건에 대해 기본 catch-all을 사용하지 마십시오. 게으르고 나중에 코드 수명에서 문제가 발생합니다.
  • 알려지지 않은 조건이 발생할 가능성은 거의 없지만 기본 catch-all을 사용하여 알 수없는 조건을 지정하십시오. 그것이 주장하는 것이 좋은 것입니다.

많은 경우에 ToLower ()를 사용하는 것이 올바른 솔루션입니다. 특히 많은 경우가 있고 해시 테이블이 생성되는 경우에 특히 그렇습니다.
Blaisorblade

"스위치 명령문에 공급할 값을 조작해야하는 경우 (전환 할 임시 변수 작성) if / else 제어문을 사용해야합니다." 좋은 충고 고마워
Sneakyness 2009


1

C #뿐만 아니라 모든 C 기반 언어는 스위치가 상수로 제한되므로 "점프 테이블"을 사용하여 매우 효율적인 코드를 생성 할 수 있다고 생각합니다. C 사례는 실제로 오래된 FORTRAN 계산 GOTO이지만, C # 사례는 여전히 상수에 대해 테스트됩니다.

옵티마이 저가 동일한 코드를 작성할 수있는 경우는 아닙니다. 예를 들어,

if(a == 3){ //...
} else if (a == 5 || a == 7){ //...
} else {//...
}

그것들은 복합 부울이므로 생성 된 코드는 값을 계산하고 단락시켜야합니다. 이제 동등한 것을 고려하십시오

switch(a){
   case 3: // ...
    break;
   case 5:
   case 7: //...
    break;
   default: //...
}

이것은로 컴파일 할 수 있습니다

BTABL: *
B3:   addr of 3 code
B5:
B7:   addr of 5,7 code
      load 0,1 ino reg X based on value
      jump indirect through BTABL+x

컴파일러에게 OR 및 동등성 테스트를 계산할 필요가 없음을 암시 적으로 알리기 때문입니다.


최적화가 구현되는 한 좋은 최적화 프로그램이 첫 번째 코드를 처리 할 수없는 이유는 없습니다. "컴파일러는 최적화 할 수 없다"는 단지 인간 만이 조정할 수있는 의미 론적 차이에 의존한다 (즉, f ()가 호출되면 f ()가 항상 0 또는 1을 반환한다는 것을 모른다).
Blaisorblade

0

내 CSS 교수는 사람들이 휴식을 잊어 버렸거나 잘못 사용했기 때문에 진술을 전환하지 말 것을 제안했습니다. 나는 그가 말한 것을 정확하게 기억할 수는 없지만 스위치 문장 (몇 년 전)의 예를 보여준 일부 코드 기반을 살펴보면 엄청난 실수가있었습니다.


C #에서는 실제로 문제가되지 않습니다. stackoverflow.com/questions/174155/… ...를 참조하십시오. 왜 두려움에
처한

0

방금 주목 한 것은 if / else 및 switch 문을 결합 할 수 있다는 것입니다! 전제 조건을 확인해야 할 때 매우 유용합니다.

if (string.IsNullOrEmpty(line))
{
    //skip empty lines
}
else switch (line.Substring(0,1))
{
    case "1":
        Console.WriteLine(line);
        break;
    case "9":
        Console.WriteLine(line);
        break;
    default:
        break;
}

3
나는 이것이 오래되었다는 것을 알고 있지만 기술적으로 당신은 아무것도 "조합하지"않고 있다고 생각합니다. 중괄호없이 "else"가 있으면 다음 명령문이 실행됩니다. 이 명령문은 일반적으로 다음 행에 들여 쓰기로 표시되는 한 줄 명령문이거나 if, switch, using, lock 등과 같은 복합 명령문 일 수 있습니다. 즉, "else if", " else switch ","else using "등을 말했듯이, 나는 그것이 의도적이고 거의 의도적으로 보이는 것을 좋아합니다. (면책 조항 : 내가 잘못 될 수도, 그래서 나는 이러한 모든 시도하지 않은!)
넬슨 ROTHERMEL에게

넬슨, 당신은 100 % 정확합니다. 이 답변을 게시 한 후 왜 이런 일이 발생했는지 알아 냈습니다.
심지어 Mien

0

다음과 같은 프로그램이 있는지 확인하십시오.

프로그램을 작성하여 (1 – 99 사이의) 임의의 숫자를 입력하고 어느 슬롯에 있는지 확인하십시오. a) 1 – 9, 슬롯 1, b – 11 – 19, 슬롯 2 c) 21-29, 슬롯 3 등 89-85까지 99

그런 다음 많은 조건을 만들어야하지만 Son 스위치 케이스를 입력 해야하는 경우

스위치 (10 개 없음)

0 = 1-9 인 경우 1 = 11-19 등

너무 쉬울거야

더 많은 그러한 예들이 있습니다!


0

switch 문은 기본적으로 평등에 대한 비교입니다. 키보드 이벤트는 코드를 쉽게 작성하고 읽을 수있을 때 switch statement에 비해 큰 장점이 있으며 if elseif 문이 있으면 {bracket}이 없으면 문제가 발생할 수 있습니다.

char abc;
switch(abc)
{
case a: break;
case b: break;
case c: break;
case d: break;
}

if elseif 문은 하나 이상의 솔루션에 적합합니다. (AmountOfApples가 5보다 크고 & AmountOfApples가 10보다 작 으면 10) 사과를 판매하는 경우 다른 경우 사과를 저장하십시오. 나는 C #이나 C ++을 쓰지 않지만 자바를 배우기 전에 배우고 그들은 가까운 언어입니다.


0

switch 문의 단점 중 하나는 여러 조건이 없다는 것입니다. if (else)에 대해 여러 조건을 가질 수 있지만 스위치에서 조건이 다른 여러 경우에는 적용 할 수 없습니다.

스위치 문은 간단한 부울 방정식 / 표현 범위를 벗어난 논리 연산에는 적합하지 않습니다. 부울 방정식 / 표현의 경우 다른 논리 연산에는 적합하지 않지만 적합합니다.

If 문에서 사용할 수있는 논리로는 훨씬 더 많은 자유가 있지만 If 문이 다루기 어려워 지거나 제대로 처리되지 않으면 가독성이 떨어질 수 있습니다.

둘 다 당신이 직면 한 상황에 따라 자리가 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.