자바스크립트에서는 문장의 끝에 세미콜론을 써도 되고 안 써도 된다. 물론, 한 코드 안에서는 세미콜론을 쓰거나 안 쓰거나 한 방식으로 통일하는 것이 바람직하다. 그렇다면, 어느 쪽으로 통일해야 할까?

요약

자바스크립트에서는 세미콜론을 굳이 넣지 않아도 된다. 나는 앞으로는 세미콜론을 생략하는 스타일로 코드를 작성할 생각이다. 이것이 과감한 주장이나 허튼 소리처럼 느껴진다면, 계속 읽어봐 주시길.

문장 부호와 문장의 끝

세미콜론의 필요성

C 계열 언어에서는 문장의 끝을 나타내기 위해 세미콜론을 사용한다. 이 방식의 장점은 코드의 스타일을 프로그래머가 좀 더 자유롭게 구사할 수 있다는 것이다. 예를 들어, 다음과 같이 C 코드를 작성해도 문법적으로 문제가 없다.

C 코드:

int       i =
1; double j =
2.0;

세미콜론은 국어 문법에서 마침표와 같은 기능을 한다. 한국어로 치면 다음과 비슷한 양식으로 글을 쓴 셈이다.

한국어 글:

밥을
먹었다. 똥을
쌌다.

개행이 제멋대로 된 탓에 둘 다 똥같은 코드와 글이지만, 우리는 이것들이 각각 세 개씩의 문장이 아니라 두 개씩의 문장으로 이루어졌다는 것을 알 수 있다. 세미콜론과 마침표라는 문장 부호 덕분에 말이다.

세미콜론은 꼭 필요한가?

그런데 위의 예를 보면 알겠지만, 코드 작성 스타일이 자유로워진다는 장점은 곧, 나쁜 스타일이 허용된다는 단점도 동시에 의미한다. 그래서 C 계열 언어의 여러 스타일 가이드에서는 예외적인 경우를 제외하고는 한 행에 하나의 문장만을 작성하도록 하는 권고를 하고 있다. 그런 권고를 따른다면 위의 코드와 글은 다음과 같이 수정해야 한다.

C 코드:

int    i = 1;
double j = 2.0;

한국어 글:

밥을 먹었다.
똥을 쌌다.

이렇게 행 하나에 문장 하나씩이 되도록 정리하면, 코드가 더 정돈되어 보이고 구조 파악이 쉽고 혼란도 덜하다. 그런데 정리하고 보니, 굳이 마침표나 세미콜론같은 문장 부호가 없더라도 어디서부터 어디까지가 한 문장인지를 알 수 있지 않은가? 행의 시작이 문장의 시작이고, 개행이 문장의 끝이니 말이다. 위 코드와 글에서 문장 부호를 빼 보자.

C 코드:

int    i = 1
double j = 2.0

한국어 글:

밥을 먹었다
똥을 쌌다

정말로, 문장 부호가 없더라도 문제가 되지 않음을 알 수 있다. (물론, 세미콜론이 없어 C 컴파일러로 위의 코드를 컴파일 할 수는 없겠지만, 스타일에 관해서만 이야기하자.) 그런데 여기서 좀 더 생각을 발전시킬 수 있다. 굳이 별도의 문장 부호를 사용하기보다, 개행으로 문장의 끝을 나타내도록 약속한다면 어떨까? 어차피 스타일 권고안에 따라 모든 문장을 각각 한 행으로 기술한다면, 세미콜론은 과잉 정보 아닌가 말이다.

이 점에 착안해 프로그래머가 특정한 스타일로 코드를 작성하도록 권하는 언어도 있다. 파이썬이 대표적이다. 파이썬은 기본적으로 문장을 한 행에 하나씩 작성하도록 한다.1 즉, 파이썬에서는 개행이 곧 문장의 끝을 나타낸다.2 덕분에 파이썬 코드는 여러 사람이 작성하더라도 스타일이 대동소이하다.

나는 문장 부호를 제공함으로써 스타일을 자유롭게 하는 것보다는, 파이썬처럼 개행으로 문장을 끝내도록 하여 스타일을 통일하는 편이 더 낫다고 생각한다.

자바스크립트에서의 세미콜론

한편 자바스크립트에서도 문장의 끝에 세미콜론을 반드시 붙이지 않아도 된다. 하지만 파이썬의 방식과는 차이가 있다. 파이썬에서는 세미콜론을 붙이지 않는 것이 기본이지만, 자바스크립트에서는 세미콜론을 붙이는 것이 기본이다.

자바스크립트에서는 엄연히 (개행이 아니라) 세미콜론으로 문장의 끝을 구별한다. 하지만 실제로는 세미콜론을 붙이지 않더라도 인터프리트 과정에서 구문 오류가 발생하지는 않는다. 그것은 인터프리터가 ‘문장의 끝이라고 생각되는 지점’에 세미콜론을 자동으로 붙여주기 때문이다. 이 기능을 세미콜론 자동 삽입(ASI, automatic semicolon insertion)이라고 한다. 이 기능을 만든 의도는 아마 초보 프로그래머들의 실수를 교정해주려는 배려였던 것 같다. 하지만 ASI는 규칙이 매우 복잡하고 동작을 예측하기 힘들어 말썽을 빚는다고 한다.

ASI로 인한 문제

인터프리터가 ‘세미콜론을 빠트렸음직한 부분’이라고 판단하는 곳은 어디일까? 그냥 코드를 작성하고 싶을 뿐인데 이런 것을 고민해야 하는 것은 참 성가시다. 하지만 그것을 무시하고 코딩하자니 ASI의 동작을 예측하지 못해 곤란을 겪을 우려가 있다. 세미콜론 논쟁의 핵심은 이런 ASI로 인한 문제를 어떻게 피할 것인가에 있는 것 같다.

세미콜론을 반드시 넣어야 한다는 사람들은 ASI의 동작을 예측하기 힘들기 때문에 모든 문장의 끝에 세미콜론을 넣으라고 한다. 반대로, 세미콜론을 넣지 않아도 된다(그리고 더 나아가 넣지 말라)고 주장하는 사람들은 ASI의 동작을 예측하기가 어렵지 않으며, 세미콜론을 명시적으로 붙이는 것과 ASI의 동작과는 관계가 없다고 한다. 각각의 주장을 따져보자.

세미콜론을 써야 한다는 주장

일단 자바스크립트의 기본 규칙은 문장의 끝에 세미콜론을 붙이는 것이다. 그래서인지, 과거에 나온 자바스크립트 교재를 보면 대부분 세미콜론을 넣는 것이 바람직하다고 설명하고 있다. 예를 들어, 『읽기 좋은 자바스크립트 코딩 기법(Maintainable Javascript)』(니콜라스 자카스 지음, 김광호 옮김)에 다음과 같은 설명이 있다. 조금 길지만 해당 내용을 전체 인용해 본다.

ASI는 코드에서 세미콜론이 필요한 자리를 찾고 없으면 세미콜론을 넣어주는데. 대부분 정확하게 찾아 문제가 없습니다. 그러나 ASI가 세미콜론을 찾는 규칙은 기억하기 어려울 정도로 복잡하므로 명시적으로 세미콜론을 넣기를 권장합니다. 디음과 같은 상황을 고려해 봅시다.

// 원래 코드
function getData() {
    return
        {
            title: 'Maintainable Javascript',
            author: 'Nicholas C. Zakas'
        }
}

// 파서가 생각하는 코드
function getData() {
    return;
        {
            title: 'Maintainable Javascript',
            author: 'Nicholas C. Zakas'
        };
}

이 예제에서 우리가 보기에 getData()는 데이터가 포함된 객체를 반환하는 힘수입니다. 하지만 ASI는 return 문 이후에 새로운 줄이 있으니 당연히 세미콜론을 삽입합니다. 따라서 getData 함수는 undefined를 반환합니다. 이 문장을 다음 예제처럼 return 문과 같은 줄에 중괄호를 표기하면 정상 값을 반환합니다.

// 세미콜론은 빠졌지만 정상 동작합니다.
function getData() {
    return {
        title: 'Maintainable Javascript',
        author: 'Nicholas C. Zakas'
    }
}

일반적으로 ASI가 수행되는 시나리오는 정해져 있습니다. 보통 두 가지 상황에서 ASI 에러가 발생하는데 ASI 동작 방식에 대한 이해 없이 코드를 작성하거나 ‘세미콜론이 없어도 ASI에서 알아서 넣어주겠지.’라는 안일한 생각으로 코드를 작성할 때입니다. 특히 경험이 부족한 개발자가 세미콜론을 빠뜨리는 실수를 자주 합니다.

더글라스 크락포드의 자바스크립트를 위한 코드 컨벤션, jQuery 코어 스타일 가이드, 구글 자바스크립트 스타일 가이드, Dojo 스타일 가이드에서도 모두 세미콜론 사용을 권장합니다. 또 JSLint와 JSHint 모두 기본적으로 세미콜론이 없으면 경고 메시지를 출력합니다.

자카스가 지적하는 것처럼, ASI의 동작을 잘 모른 채로 특정한 스타일로 코드를 작성할 경우 ASI로 인한 오류를 겪을 수 있다. 그런데 곰곰이 따져 보면, 문제를 일으키는 스타일은 ‘세미콜론을 넣느냐 안 넣느냐’가 아니라, ‘어디에서 개행하느냐’라는 것을 알 수 있다. 자카스가 ASI 문제를 우회한 코드에서도 세미콜론은 붙이지 않았다. (“세미콜론은 빠졌지만 정상 동작합니다.”라는 주석은 원문에서 그대로 인용한 것.) 즉, 세미콜론을 모든 문장의 끝에 붙인다 하더라도 자카스가 소개한 ASI 문제는 그대로 겪을 수 있다는 말이다.

결국 세미콜론을 넣느냐 마느냐는 기호의 문제이지, ASI 문제를 피하는 것과는 관계 없는 것 아닌가?

세미콜론을 쓰지 않아도 된다(쓰지 말아야 한다)는 주장

내가 세미콜론에 대해 다시 생각하게 된 것은 유명 자바스크립트 라이브러리에서 세미콜론을 사용하지 않는 코드를 종종 봤기 때문이다. 그 전까지는 세미콜론을 전혀 넣지 않는 자바스크립트 코드는 생각해 본 적도 없었기에 이상하게 생각되었다. 고집스럽게 세미콜론을 계속 넣기보다는 고수들이 왜 세미콜론을 안 넣는지 찾아 봐야 했다.

몇몇 블로그 글들을 읽어봤는데, JavaScript Standard Style(코드 형식 통일 프로그램)을 만든 Feross Aboukhadijeh의 글 「Never Use Semicolons」이 가장 근거가 분명하고 이해하기 쉬웠다. 장황한 내 글보다 훨씬 간결하니 직접 읽어보셔도 좋다. 그의 논점을 간단히 요약하자면, ASI와 명시적 세미콜론 삽입 스타일은 연관이 없으며, 세미콜론을 붙이는 것보다는 ASI의 동작을 이해하는 것이 중요하다는 것이다.

자카스도 지적했듯이, ASI는 대개는 적절한 위치를 추론하기 때문에 문제를 자주 일으키지는 않는다. 게다가 자카스가 든 예도 세미콜론을 명시적으로 붙여야 할 근거가 되기 보다는, ASI 문제가 특정한 코딩 스타일에서 발생한다는 것을 보여줄 뿐이다. ASI는 우리가 세미콜론을 명시적으로 넣든 안 넣든, 동작할 곳에서 동작한다. ASI 문제를 피하려면 ASI의 동작 원리를 이해하고 그에 맞게 코딩 스타일을 정립하는 것이 핵심이지, 세미콜론을 넣는 것을 강제할 필요는 없다.

그런데 Aboukhadijeh는 세미콜론을 넣지 않아도 된다는 데서 더 나아가, 세미콜론을 쓰지 말아야 한다고 주장하는 데까지 나아간다. 그 근거는 “모든 문장에 세미콜론을 넣어라”라는 규칙이 생각보다 따르기가 쉽지 않다는 것이다. 예를 들어, 다음과 같이 세미콜론을 붙이면 안 되는 위치들이 있다. (「Never Use Semicolons」에서 인용한 코드)

function foo () {
  return 42; // ok
};           // <– AVOID!

함수 정의는 그나마 간단하지만 클래스 정의 구문에서는 더 성가시다. (역시 「Never Use Semicolons」에서 인용한 코드)

class Foo {
  constructor () {
    if (baz) {
      return 42; // ok
    };           // <– AVOID!
    return 12;   // ok
  };             // <– AVOID!
};               // <– AVOID!

물론, 충분한 주의를 기울이고 편집기와 Lint 도구 등의 도움을 받으면 세미콜론을 찍을 위치와 안 찍을 위치를 가려내는 것이 크게 어렵지는 않다. 하지만 굳이 세미콜론을 고집해야 할까? 세미콜론을 아예 생략하기로 한다면 이런 성가신 문제를 전혀 신경쓰지 않아도 된다. 바로 이 순간, ASI가 일을 방해하는 기능에서 정말로 편의를 제공하는 기능으로 탈바꿈한다!

게다가 Aboukhadijeh에 따르면, ASI가 일을 방해하지 않도록 하려면 딱 한 가지 규칙만 기억하면 된다고 한다. 그것은 바로 행의 시작을 [, (, `으로 하지 않는 것이다. 꼭 그렇게 해야 한다면, 다음과 같이 세미콜론으로 행을 시작하면 된다고 한다. (역시 「Never Use Semicolons」에서 인용한 코드)

;[1, 2, 3].forEach(bar)

물론 이런 코드를 작성해야 할 경우는 거의 없을 것이니 안심해도 된다. 자바스크립트에서 식(expression)만을 덩그러니 써 놓는 코드를 쓸 일이 거의 없을 테니까 말이다. 즉, 대개는 let foo = [1, 2, 3].forEach(bar), 또는 return [1, 2, 3].forEach(bar)와 같이 문을 나타내는 키워드를 먼저 쓰기 마련이다.

결론

자바스크립트에서는 세미콜론을 굳이 넣지 않아도 된다. 그 전까지는 세미콜론을 반드시 넣자는 것이 압도적 다수파였던 것 같지만, 현재는 세미콜론을 넣지 말자는 주장도 힘이 커지고 있다. 여전히 논쟁거리인 것 같긴 하다. 내가 보기에는 세미콜론을 넣지 않아도 된다는 주장이 좀 더 설득력 있게 느껴진다. 나는 앞으로는 세미콜론을 생략하는 스타일로 코드를 작성할 생각이다.

참고

니콜라스 자카스의 책 『읽기 좋은 자바스크립트 코딩 기법(Maintainable Javascript)』의 일부 내용을 비판하기는 했지만, 전체적으로 매우 훌륭한 책이다. 번역도 꼼꼼하게 잘 되어 있다. 나도 이 책에서 많은 도움을 받았고, 이 책에서 제시하는 코딩 스타일을 대부분 따르고 있다. 자바스크립트 코딩 스타일에 관심이 있다면 읽어봐도 좋다.

세미콜론을 꼭 써야 한다고 생각하는 근거가 있거나, 내가 쓴 글에서 잘못된 내용이 있다면 알려주시기 바란다.

P.S.

그런데 간단히 정리하고 싶지만 또 그러기가 어려운 것이…

TC39(ECMA 인터내셔널의 ECMA 스크립트 담당 기술 위원회)에서는 향후에 언어가 확장되면서 발생할 수 있는 문제를 방지하기 위해서, 세미콜론을 넣는 스타일을 권장하는 듯하다.

나는 일단은 세미콜론 생략을 해 보려 하지만 나중에 가면 또 분위기가 변할 수도 있을 것 같다.

  1. 원한다면 파이썬에서도 세미콜론으로 문장의 끝을 표시하여 한 행에 여러 문장을 서술할 수도 있지만, 가능하다는 것이지 권장사항은 아니다. 

  2. 값·식·문이 한 행에서 끝나지 않는 경우는 예외. 이와 관련된 구문 규칙은 합리적이고 파서가 잘 해석한다.