세미콜론을 둘러싼 끝나지 않는 토론
프로그래밍을 처음 배울 때 가장 많이 마주치는 에러 중 하나가 "세미콜론을 빼먹었습니다"일 겁니다. C, Java, JavaScript 같은 언어에서 세미콜론(;)은 문장의 끝을 알려주는 필수 구문입니다. 그런데 Python, Go, Kotlin, Swift 같은 현대 언어들은 세미콜론 없이도 잘 동작합니다. 과연 세미콜론은 프로그래밍에 정말 필요한 것일까요? 최근 이 주제를 깊이 있게 다룬 기술 블로그 글이 개발자들 사이에서 활발한 논의를 불러일으켰습니다.
세미콜론은 왜 존재하는가
세미콜론의 역할을 이해하려면 파서(Parser)의 관점에서 생각해야 합니다. 컴파일러나 인터프리터가 소스 코드를 읽을 때, 어디서 하나의 문장(statement)이 끝나고 다음 문장이 시작되는지 알아야 합니다. 세미콜론은 이 경계를 명시적으로 표시하는 가장 단순한 방법입니다.
1970년대 C 언어가 설계될 때, 세미콜론은 컴파일러를 단순하게 만들어주는 실용적 선택이었습니다. 당시 컴퓨터의 메모리와 처리 능력은 극히 제한적이었고, 파서가 "이 줄이 아직 끝나지 않았을 수도 있다"는 가능성을 추론하는 것은 불필요한 복잡성이었습니다. 세미콜론을 찍으면 파서는 단순히 그 지점에서 문장을 잘라내면 됩니다.
C의 영향을 받은 C++, Java, C#, JavaScript 등이 모두 이 관례를 이어받았고, 수십 년간 세미콜론은 "코드를 작성하는 기본 규칙"으로 자리 잡았습니다.
세미콜론 없는 세계는 어떻게 가능한가
세미콜론이 없는 언어들은 대안적인 방법으로 문장 경계를 판단합니다. 크게 세 가지 접근법이 있습니다.
첫째, 줄바꿈을 구분자로 사용하는 방식입니다. Python이 대표적입니다. 코드를 한 줄에 한 문장씩 작성하면, 줄바꿈이 곧 문장의 끝을 의미합니다. 한 문장이 여러 줄에 걸쳐야 할 때는 백슬래시(\)나 괄호 안에서 자동으로 연속되는 규칙을 사용합니다. 이 방식의 장점은 코드가 시각적으로 깔끔해진다는 것이고, 단점은 긴 표현식을 여러 줄로 나눌 때 때때로 명시적인 연속 표시가 필요하다는 것입니다.
둘째, ASI(Automatic Semicolon Insertion) 방식입니다. JavaScript가 이 방식을 사용합니다. 문법적으로 세미콜론이 필요하지만, 특정 규칙에 따라 파서가 자동으로 세미콜론을 삽입해줍니다. 이 방식은 악명 높은 문제를 일으키기도 합니다. 예를 들어 return 다음에 줄바꿈을 넣으면, 의도와 달리 return undefined로 해석될 수 있습니다. JavaScript의 ASI는 "세미콜론을 안 써도 된다"와 "세미콜론을 써야 한다" 진영 간의 논쟁을 20년 넘게 이어오게 만든 원인이기도 합니다.
셋째, Go 언어의 접근법입니다. Go는 렉서(Lexer) 단계에서 특정 토큰 뒤에 줄바꿈이 오면 자동으로 세미콜론을 삽입합니다. 식별자, 숫자 리터럴, return, ), } 같은 토큰 다음에 줄바꿈이 오면 세미콜론이 자동 삽입됩니다. JavaScript의 ASI보다 규칙이 단순하고 예측 가능하다는 장점이 있습니다. 대신 이 규칙 때문에 Go에서는 중괄호의 위치가 강제됩니다. {를 다음 줄에 내리는 코딩 스타일을 쓸 수 없는 이유가 바로 이것입니다.
세미콜론 논쟁의 본질
세미콜론 논쟁은 단순히 "타이핑을 한 번 더 하느냐 마느냐"의 문제가 아닙니다. 이 논쟁의 본질은 명시성(explicitness) vs 간결성(brevity) 사이의 트레이드오프입니다.
세미콜론을 지지하는 쪽은 코드의 의도를 명확히 표현하는 것이 중요하다고 주장합니다. 암묵적 규칙에 의존하면 edge case에서 예상치 못한 버그가 발생할 수 있고, 특히 팀 프로젝트에서 모든 구성원이 ASI 규칙을 완벽히 이해하고 있으리라 기대하기 어렵다는 것입니다.
반대쪽에서는 세미콜론이 시각적 노이즈라고 반박합니다. 코드의 논리 흐름을 읽는 데 세미콜론은 아무런 정보를 추가하지 않으며, 현대 편집기와 린터가 문법 오류를 즉시 잡아주기 때문에 인간이 직접 구분자를 입력할 필요가 줄어들었다는 것입니다.
흥미로운 점은, 최근 등장하는 언어일수록 세미콜론을 선택적이거나 불필요하게 만드는 경향이 뚜렷하다는 것입니다. Kotlin은 세미콜론이 선택적이고, Swift도 마찬가지입니다. Rust는 세미콜론이 필수이지만, 이는 세미콜론이 표현식(expression)과 문장(statement)을 구분하는 의미론적 역할을 하기 때문입니다. Rust에서 세미콜론을 빼면 해당 줄이 값을 반환하는 표현식이 되므로, 세미콜론이 단순한 구분자를 넘어 프로그램의 의미를 바꾸는 도구가 됩니다.
한국 개발자에게 주는 시사점
실무에서 이 논쟁이 가장 직접적으로 영향을 미치는 영역은 팀 코딩 컨벤션입니다. JavaScript/TypeScript 프로젝트에서 ESLint의 semi 규칙을 always로 할지 never로 할지는 거의 모든 팀이 한 번쯤 논의하는 주제입니다. 정답은 없지만, 중요한 것은 팀 내에서 일관성을 유지하는 것입니다. Prettier 같은 포매터를 도입하면 이 논쟁 자체를 자동화로 해결할 수 있습니다.
새로운 언어를 배울 때도 이 맥락을 알면 도움이 됩니다. 예를 들어 Go에서 중괄호를 다음 줄에 내릴 수 없는 이유, Rust에서 함수 마지막 줄에 세미콜론을 빼야 값이 반환되는 이유 등은 각 언어의 파서 설계 철학을 이해하면 자연스럽게 납득됩니다.
마무리
세미콜론은 50년 전 하드웨어 제약에서 태어난 실용적 선택이었지만, 오늘날에는 언어 설계 철학과 개발자 경험(DX)의 문제로 진화했습니다. 명시성과 간결성 사이에서 각 언어가 어떤 선택을 했는지 살펴보면, 그 언어의 설계 사상을 이해하는 좋은 창이 됩니다.
여러분은 세미콜론 있는 쪽과 없는 쪽 중 어디를 선호하시나요? 그리고 팀에서 이 컨벤션을 어떻게 결정하셨는지 경험을 나눠주시면 재미있을 것 같습니다.
🔗 출처: Hacker News
"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"
실제 수강생 후기- 비전공자도 6개월이면 첫 수익
- 20년 경력 개발자 직강
- 자동화 프로그램 + 소스코드 제공