Any의 함정 TypeScript와 Zod를 활용한 Type 명세로 해소

August 21, 2024 김효진 조회수 2,304

오늘은 TypeScript와 Zod에 관한 주제로 이야기하고자 합니다. TypeScript는 JavaScript의 동적 타이핑 대신 정적 타이핑을 도입해 코드의 안정성과 유지보수성을 높인 언어입니다. 하지만 TypeScript에서도 any 타입을 남발하다 보면 문제가 생길 수 있습니다. 이러한 문제를 방지하기 위해 세밀한 타입 지정과 유효성 검사를 지원하는 Zod라는 라이브러리를 함께 사용하는 것이 좋습니다. 이 과정에서 생성형 AI의 도움을 받아 작업을 간소화할 수 있습니다. 자세한 내용은 본문에서 함께 알아보도록 하겠습니다.

동적 타이핑 언어, JavaScript

지금으로부터 약 30년 전인 1990년대 초반에 당시 대부분 정적인 HTML로 이루어진 웹 페이지들을 동적으로 만들기 위한 스크립트 언어가 개발되었습니다. Java와 크게 연관성은 없지만 Java 언어의 인기에 힘입어 마케팅적 요소를 담은 JavaScript라는 명칭으로 큰 성공을 거두었죠. Brendan Eich에 의해 개발되고 초기에는 LiveScript로 불리던 이 스크립트 언어는 웹 브라우저상에서 동작하는 스크립트 언어로 시작했으나 현재는 서버 사이드 개발, 모바일 앱 개발 등 많은 영역에서 사용되는 범용 프로그래밍 언어로 발전하였습니다. 문법이 비교적 간단하고 동적 타입 언어로서 엄격한 규칙이 적어 진입 장벽이 낮으며 폭넓은 개발 생태계에 따른 활발한 커뮤니티가 있는 등 많은 장점을 갖지만 이러한 JavaScript에도 분명히 맹점이 있습니다. 특히 복잡하고 큰 규모의 프로젝트에서는 동적 타입 언어가 큰 단점으로 작용하여 디버깅이 어렵고, 변수나 함수의 역할이 명확하게 정의되지 않은 경우 가독성이 떨어질 뿐 아니라 유지 보수도 힘들어집니다.


TypeScript의 등장

이러한 문제점을 개선하기 위해 Microsoft 주도하에 개발된 TypeScript는 JavaScript의 SuperSet으로서 JavaScript의 동적 타이핑 대신 정적 타이핑을 도입했습니다. TypeScript라는 이름에서 알 수 있듯 Type을 보다 엄격히 정의 하고 *컴파일 시점에 오류를 쉽게 발견할 수 있도록 개선하였기 때문에 런타임 오류가 발생할 가능성이 줄어듭니다. 정적 타이핑을 채택한 TypeScript를 사용하면 개발자가 코드 내에서 변수와 함수, 클래스 등의 Type을 명시적으로 선언할 수 있어 결과적으로 코드의 안정성과 유지 보수성이 크게 향상됩니다. 또한 어떤 함수가 어떤 매개변수를 가지며 어떤 Type을 반환하는지 등을 명확하게 나타내어 코드의 가독성과 이해도가 높아집니다. 아울러 이러한 정보를 기반으로 개발 도구를 통한 코드 자동 완성이 가능하므로 생산성이 증대됩니다. 이처럼 TypeScript는 JavaScript의 한계를 극복하며 대형 프로젝트에 적합한 강력한 기능을 제공합니다.



*이해를 돕기 위해 ‘컴파일’이라고 지칭하였으나 엄밀히 말하자면 TypeScript는 High-Level 언어 간의 변환이므로 ‘트랜스파일’입니다.

Any의 남용

그러나 이처럼 개선된 TypeScript에서도 Type 정의를 생략하거나 명확하게 지정하기 어려울 때 any라는 타입을 사용하게 됩니다. 예를 들어, 예상치 못한 오류나 REST API 호출을 통해 받은 응답 데이터 또는 잦은 변경이 예측되는 데이터를 처리할 때 any 타입을 사용하곤 합니다. 이렇게 하면 개발 초기에는 덜 번거롭고 편할 수 있지만 프로젝트의 규모가 커지고 협업하는 개발자가 많아질수록 여러 가지 문제가 일어납니다. any로 선언한 변수는 어떠한 값이든 저장할 수 있기 때문에 의도치 않게 잘못된 값을 할당할 수 있으며 변수가 가져야 할 값이 무엇인지 파악하기 힘듭니다. 또한 타입 체크를 느슨하게 하므로 코드상의 실수를 조기에 발견하기 어렵습니다. 이로 인해 버그 발견이 어렵고 코드 리뷰 시 어려움을 겪게 됩니다. 따라서 TypeScript의 장점을 극대화하기 위해 개발 초기 단계에는 조금 불편하고 귀찮더라도 any 타입의 사용을 최소화하고 가능하면 구체적인 타입을 사용하는 것이 좋습니다.


Zod 소개

Zod는 JSON Schema를 기반으로 하는 강력한 타입 검증 도구로서 세밀한 타입 지정과 유효성 검사를 지원하는 라이브러리입니다. 이를 통해 숫자 범위나 문자열 길이 제한 등과 같은 단순한 type 검사뿐만 아니라 복잡한 validation까지 가능합니다. TypeScript와 Zod를 함께 사용하면 초기 설정 비용이 다소 많이 들 수 있으나, 장기적으로 코드 품질 향상과 유지 보수 용이성 측면에서 큰 이득이 있습니다. 특히 대규모 프로젝트에서는 이러한 장점이 더욱 부각될 것입니다. 그럼 TypeScript와 Zod를 어떻게 함께 사용하면 되는지 살펴보도록 하겠습니다.



Zod로 스키마 정의부터 유효성 검사까지

Zod에서 스키마를 정의하는 것은 데이터의 구조와 타입을 명확하게 지정하는 작업입니다. 또한 추가적으로 유효성 검사도 가능합니다. TypeScript에서 Zod를 어떻게 활용할 수 있는지 간단한 예시를 들어 살펴보겠습니다. 우선 userInfo라는 Schema를 만들고, password가 8자 이상의 문자열인지 그리고 email이 유효한 형식인지 확인하는 간단한 유효성 검사를 구현합니다.




아래와 같이 의도적으로 잘못된 형식의 데이터를 기입한 후 코드를 실행하면 ZodError에서 “String must contain at least 8 character(s)”, “Invalid email”과 같이 상세한 내용으로 Error가 발생하여 어떤 부분이 잘못되었는지 명확히 알 수 있습니다.




위의 예시는 Schema를 직접 정의한 경우입니다. 하지만 더욱 편리하게 초안을 만들 수 있는 좋은 방법이 있습니다. 바로 생성형 AI를 활용하는 것입니다.



생성형 AI를 활용한 Zod 스키마 정의 및 유효성 검사

TypeScript Type 지정부터 Zod까지 너무 많은 공수가 들지 않을까 걱정되실 수도 있는데, Zod의 schema나 유효성 검사의 초안 정도는 생성형 AI를 활용하여 간단하게 생성할 수 있습니다.

제가 주로 사용하는 프롬프트는 다음과 같습니다. 그대로 사용해도 괜찮고 필요에 맞게 수정하여 사용하셔도 됩니다.




위와 같은 프롬프트를 생성형 AI에 입력하면 아래와 같은 코드 초안을 생성형 AI가 작성해줍니다. 물론 완벽하지 않을 수도 있고, 특히 유효성 검사는 실제 개발자의 정확한 의도대로 진행하려면 초안에서 추가적으로 수정해야 할 수도 있습니다. 다음 내용은 실제 생성형 AI에 의해 생성된 답변입니다.




바로 코드에 적용해보려고 할 때 실제 사용 중인 Zod 버전과 다른 버전의 내용을 생성하여 몇 가지 사항을 직접 수정해야 하는 경우도 있지만 초안으로 사용하기에는 충분합니다.




생성형 AI가 만들어준 성공 케이스와 실패 케이스의 dummy data를 활용해 최종적으로 확인해보고 사용하면 편리합니다.




실패 케이스로 id를 아예 넘기지 않고, 비밀번호는 5자리, 이름은 1글자, 나이는 음의 정수, email은 유효하지 않은 형식으로 입력하여 테스트해보면 ZodError가 발생하며 각각 상세한 이유로 실패했음을 인지할 수 있습니다.



마치며

TypeScript와 Zod를 함께 사용하려면 초기 투자 시간이 필요하며, 이 포스팅에서 보여드린 것처럼 생성형 AI의 도움을 받아 어느 정도 작업을 간소화할 수 있습니다. 이러한 작은 투자들이 장기적인 관점에서 기술 부채(Technical Debt)를 줄이고 안정성을 높이는 데 기여한다고 생각됩니다. 특히 대규모 프로젝트에서 코드 품질 관리를 용이하게 하고 팀원 간 코드 가독성을 높여 큰 보탬이 될 수 있습니다. 앞으로 이 두 도구를 효과적으로 활용하여 코드 품질을 향상시키고, 더 나은 소프트웨어를 개발해 보세요.




저자

김효진

SE Lab(VD)

이메일 문의하기