프로그래밍 언어에서 연산자 우선순위는 양날의 검이다. a + b * c처럼 수학 교육으로 몸에 밴 규칙은 괄호 없이도 누구나 읽을 수 있지만, 비트 연산자와 비교 연산자, 논리 연산자가 뒤섞이기 시작하면 이야기가 달라진다. 숙련된 개발자라도 머릿속 우선순위 표를 더듬다 결국 레퍼런스를 찾아보게 되고, 바로 그 '잘못 기억한 우선순위'가 조용한 버그의 온상이 된다. Zig 커뮤니티는 이 문제를 오래전부터 인식하고 있었다. 2016년 2월 8일에 올라온 이슈 #114, 즉 '혼란스러운 우선순위를 명확히 하기 위해 때때로 괄호를 강제하자'는 제안이 그것이다. 이 제안은 10년이 지난 지금, 기여자 CMDRZero의 구현으로 Zig의 master 브랜치에 반영됐다.
모든 조합에 우선순위를 두지 않는다
이번 변경의 핵심은 발상의 전환이다. 기존 언어들은 거의 모든 연산자 조합에 우선순위를 '정의'해 두고 프로그래머가 그것을 외우도록 요구한다. Zig는 반대로, 일부 연산자 조합에 대해서는 우선순위를 아예 정의하지 않기로 했다. 우선순위가 정의되지 않은 조합을 쓰면 컴파일러가 괄호를 요구하며 거부한다. 덧셈·뺄셈과 곱셈·나눗셈처럼 직관에 어긋나지 않는 흔한 경우는 여전히 괄호 없이 쓸 수 있다. 예를 들어 나머지 연산자 %는 덧셈·뺄셈에 대해서는 우선순위가 명확하지만, 곱셈·나눗셈과 섞이거나 자기 자신과 섞일 때는 괄호가 필요하다.
이 설계의 기준은 '놀라움을 줄인다'는, 다소 주관적이지만 분명한 원칙이었다. 핵심은 개발자가 모호한 조합의 규칙을 외울 필요가 없다는 점이다. 앞으로 읽게 될 코드에는 이미 괄호가 붙어 명확하게 정리되어 있을 것이고, 직접 작성하는 코드에서는 괄호가 필요한 순간마다 컴파일러가 알려 준다. 결과적으로 개발자가 기억해야 할 규칙은 산술 연산자처럼 우선순위가 여전히 명확하게 정의된 조합으로 좁혀진다.
실제 영향은 생각보다 작다
언어의 근본 문법을 건드리는 변경이라 코드베이스 전반에 파장이 클 것 같지만, 실측치는 그렇지 않다. Zig 자체 코드베이스는 100만 줄이 넘지만, 이번 변경으로 수정이 필요했던 것은 353줄에 불과했다. 다시 말해 괄호를 새로 요구받게 되는 표현식은 실무에서 비교적 드물게 나타난다. 기존 코드를 옮길 때는 두 가지 선택지가 있는데, 원저자는 직접 의도한 평가 순서를 다시 따져 괄호를 붙이는 방식을 권한다. 그 과정에서 이미 숨어 있던 버그가 드러나는 경우가 있기 때문이다. 자동 변환 도구인 zig fmt --upgrade를 쓰는 길도 있지만, 이 경우에도 변환 후 diff를 훑어보며 의도와 다르게 바뀐 부분이 없는지 확인하라고 조언한다.
특히 주목할 만한 사례가 a orelse b() catch c 같은 표현이다. 기존 규칙에서 이 코드는 (a orelse b()) catch c로 평가됐는데, 실제 의도는 a orelse (b() catch c)인 경우가 더 많다. 평가 결과가 바뀌지 않더라도 괄호를 명시하는 편이 의미를 더 정확히 드러낸다는 것이다. 다만 자동 변환은 때로 불필요한 괄호까지 덧붙인다. a orelse ((b + c) - d)처럼 b + c를 감싼 괄호는 원래 필요 없는데도 추가될 수 있어, 깔끔한 코드를 원한다면 약간의 수작업 정리가 남는다.
실무자에게 주는 의미
이 변경은 Zig가 지향하는 가치, 즉 '명시성'과 '읽는 사람의 인지 부담 최소화'를 문법 차원에서 밀어붙인 사례다. C나 그 계열 언어에서 비트 연산자와 비교 연산자를 섞을 때 발생하는 고전적 함정을 떠올리면, 컴파일러가 모호함을 선제적으로 막아 준다는 접근은 유지보수 관점에서 분명한 이점이 있다. 코드 리뷰에서 '이 표현식의 평가 순서가 맞나?'를 두고 소모하던 논쟁이 줄고, 우선순위를 잘못 기억해 생긴 버그가 애초에 발생하기 어려워진다.
물론 한계도 있다. 어떤 조합에 괄호를 요구할지는 '놀라움을 줄인다'는 주관적 판단에 기대고 있어, 다른 언어에 익숙한 개발자에게는 Zig만의 경계선을 새로 익히는 비용이 생긴다. 또한 이 기능은 아직 master 브랜치 단계이며, 전체 우선순위 표와 다른 연산자에 대한 상세 규칙은 언어 레퍼런스의 갱신된 Precedence 절을 직접 확인해야 한다. 그럼에도 '외워야 할 규칙을 늘리는 대신 줄인다'는 방향은, 언어 설계가 개발자의 기억력이 아니라 도구의 피드백에 의존하도록 옮겨 가는 흐름을 잘 보여 준다. 문법 변경에 대한 거부감보다 그 의도를 읽는 편이 실무적으로 유익하다.