"Cannot find symbol"이라는 컴파일 에러의 의미는 무엇인가요?

조회수 48273회

"Cannot find symbol" 에러에 대해서 몇 가지 설명을 부탁드립니다.

  • 이 에러의 의미가 무엇인가요?
  • 이 에러는 왜 발생하나요?
  • 개발자는 이 에러를 어떻게 해결하나요?

1 답변

  • 좋아요

    1

    싫어요
    채택 취소하기

    1. 이 에러의 의미가 무엇인가요?

    먼저, 이것은 컴파일 에러입니다. 작성하신 자바 소스 코드 내의 문제일 수도 있고, 혹은 컴파일 방법의 문제일 수도 있습니다.

    자바 소스 코드는 다음의 것들로 구성됩니다:

    • 키워드: true, false, class, while, 등
    • 리터럴: 42, 'X', "Hi mum!"
    • 연산자 혹은 비문자 토큰들: +, =, {, 등
    • 식별자: Reader, i, toString, processEquibalancedElephants, 등
    • 주석과 공백문자들

    "Cannot find symbol"은 식별자에 대한 에러입니다. 컴파일러가 소스코드를 컴파일 할 때, 컴파일러는 소스코드에서 각각의 모든 식별자가 무엇을 의미하는지를 알아야 합니다.

    "Cannot find symbol"은 컴파일러가 해당 식별자의 의미를 이해하지 못함을 나타내는 것이지요. 즉, 소스코드는 컴파일러가 이해하지 못하는 식별자를 사용한 것로 판단되는 것이지요.

    2. 이 에러는 왜 발생하나요?

    이 에러가 발생하는 원인은 하나입니다. 컴파일러는 해당 식별자가 어디에 선언되어 있는지를 찾아봅니다. 그런데 그 선언을 찾지 못하면 이 에러가 발생합니다. 이런 상황은 여러 가지 경우에 발생될 수 있습니다.

    • 식별자에 대해서:

      • 철자가 잘못된 경우: 예를 들어 StringBuilder라는 이름 대신에 StringBiulder라고 작성한 경우. 자바는 철자를 잘못 작성한 경우는 자동으로 보정해주지 않습니다.
      • 대소문자 구분을 잘못한 경우: 예를 들어 StringBuilder 대신에 stringBuilder라고 작성한 경우. 자바의 모든 식별자는 대소문자를 구분해야 합니다.
      • 밑줄 (_)을 사용한 경우: 예를 들어, mystringmy_string은 다른 식별자입니다. (자바의 이름 명명 규칙을 충실히 따른다면, 대부분의 이런 실수는 미연에 방지될 수 있습니다)
    • 변수를 참조하는 식별자에 대해서:

      • 변수를 선언하지 않은 경우:
      • 변수를 사용하는 시점에, 변수의 사용 범위를 벗어난 경우 (아래 예제를 참고하세요)
    • 메소드 이름에 대해서:

      • 슈퍼클래스 혹은 인터페이스에 선언되지 않은 메소드를 호출하려고 할 경우
    • 클래스 이름에 대해서:

      • 클래스를 import 하지 않은 경우
      • "star"를 사용하여 import 했을 때, 해당 패키지 않에 클래스가 선언되지 않은 경우
      • 클래스의 객체를 생성할 때 new 키워드를 사용하지 않은 경우

      String s = String(); // should be 'new String()'

    이러한 문제들은 복합적으로 발생할 수 있습니다. 예를 들어, "star"를 사용해서 java.io.* 패키지를 import 하고, java.io 패키지가 아닌 java.nio 패키지에 정의된 Files 클래스를 사용하려고 할 때 에러가 발생할 수 있습니다. 또는, java.io 패키지의 File 클래스를 사용하려고 했는데 실수로 Files 클래스를 사용하였을 때도 이러한 문제가 발생할 수 있지요.


    다음은 변수의 사용 범위가 벗어났을 때 "Cannot find symbol" 에러가 발생될 수 있는 경우의 예입니다.

    for( int i = 0; i < strings.size(); i++) {
       if(strings.get(i).equalsIgnoreCase("fnoord")) {
          break;
       }
    }
    if(i < strings.size()) {
       ...
    }
    

    if문의 i에 대해서 "Cannot find symbol"이라는 에러가 발생할 것입니다. if문 이전에 선언된 ifor문 안에서 선언되어 해당 반복문 안에서만 사용할 수 있기 때문입니다. 그래서 if문 안의 ii의 선언문을 볼 수 없습니다. 이와 같은 경우를 "변수의 사용 범위가 벗어났다"라고 표현하지요.

    (이 문제를 해결하기 위해서는 if문을 반복문 안에서 정의하거나, i를 반복문 시작 전에 선언하면 됩니다.)


    만약 터미널에서 커맨드라인 (command line)으로 컴파일한다면, 컴파일러가 해당 기호 (symbol)을 찾지 못하는 또다른 이유도 존재할 수 있습니다. 간단하게 특정 클래스들을 컴파일 하지 않았을 경우 문제가 발생할 수 있습니다. 예를 들어, Bar 클래스와 Bar 클래스를 사용하는 Foo 클래스가 있다고 가정했을 때, Bar 클래스는 컴파일 하지 않고 Foo 클래스만 java Foo.java로 컴파일 한 경우, 컴파일러는 Bar 를 찾을 수 없다는 에러를 발생하게 됩니다. 이를 해결하기 위해서는 FooBar 클래스 모두 컴파일을 해야 겠지요. java Foo.java Bar.java 또는 javac *.java와 같이요. 그래서 커맨드라인으로 직접 컴파일 하는 것 보다는 Ant, Maven, Gradle 등과 같은 자바를 컴파일해주는 도구를 사용하시는 것이 더 좋습니다.

    3. 개발자는 이 에러를 어떻게 해결하나요?

    일반적으로 문제의 원인이 무엇인지 이해하는 것부터 시작해야 합니다. 그리고 나서 소스코드가 구현하고자 하는 것이 무엇인지 생각하고, 마지막으로 구현하고자 하는 것을 이행할 수 있도록 수정해야 합니다.

    다음의 예를 보면:

    for(int i = 1; i < 10; i++) {
       for(j = 1; j < 10; j++) {
    

    } }

    
    컴파일러가 `j`에 대해서 "Cannot find symbol" 에러를 발생할 것을 예상할 수 있을 것입니다. 이 문제를 해결하기 위한 방법은 여러가지가 있을 수 있습니다.
    
    * 안쪽 `for`문을 `for(int j = 1; j < 10; j++)`와 같이 수정할 수 있습니다.
    * 안쪽 `for`문 전에 `j`를 선언하거나, 바깥쪽 `for`문 전에 `j`를 선언할 수도 있습니다. 
    * 안쪽 `for`문의 `j`를 `i`로 변경할 수도 있습니다. 그러나 이것은 본래 의도하지 않은 결과를 초래할 수도 있습니다.
    * 등등
    
    ### 4. 모호한 원인들
    
    "Cannot find symbol" 에러가 발생한 곳이 애매한 경우도 있습니다.
    
    1. *잘못된 소스 코드를 사용하고 있는 경우*: 
    신입 자바 개발자들은 자바 개발 도구 (IDE, Ant, Maven Gradle 등)가 어떻게 동작하는지를 이해하지 못하고 개발하는 경우들이 종종 있습니다. 이런 경우, 프로그래머는 소스코드를 재컴파일 하지 않는 등의 문제로 발생한 에러를 소스코드 상에서 에러를 수정하기 위해 애쓰는 문제가 발생할 수도 있는 것이지요.
    
    2. 시스템 클래스들의 재정의:
    컴파일러가 `substring` 메소드에 대해서 다음과 같은 상황에 모르는 기호 (unknown symbol)이라는 에러를 발생하는 경우도 있습니다.
    
    

    String s = ... String s1 = s.substring(1);

    
    개발자가 `String`이라는 이름의 클래스를 정의하고, 그 클래스에는 `substring`이라는 메소드는 정의하지 않은 경우 자바의 `String` 클래스와 이름이 중복되어 에러가 발생할 수 있는 것이지요.
    
    일반적인 라이브러리에 정의된 클래스와 동일한 이름의 클래스는 만들지 않는 것이 좋습니다!
    
    3. 호모글리프 (homoglyph)
    
    만약에 소스파일을 UTF-8으로 인코딩해서 만들었는데, 호모글리프 때문에 실제로는 다른 문자가 동일한 문자로 인식되는 경우가 있을 수 있습니다. 
    `\uxxx` 문자를 사용해서 소스파일이 ASCII 혹은 Latin-1으로 인코딩되는 것을 피할 수 있습니다.
    

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)