TypeScript any 남발 사건, 또는 선배한테 혼난 날

TypeScript any 남발 사건, 또는 선배한테 혼난 날

TypeScript any 남발 사건, 또는 선배한테 혼난 날

사건의 발단

오늘 아침에 PR 올렸다. 회원가입 폼 기능 추가. 이틀 걸렸다.

자신 있었다. 일단 돌아갔으니까. 로그인도 되고 회원가입도 되고. 테스트도 다 통과했다. 내가 봐도 깔끔했다.

점심 먹고 돌아왔는데 슬랙 알림 6개. 심장이 쿵 내려앉았다.

“이신입님, PR 확인 부탁드립니다.”

선배 멘션이었다. 그것도 김민수 선배. 우리 팀에서 제일 무서운 분.

클릭했다. 코멘트 12개. 빨간 줄이 화면을 가득 채웠다.

첫 줄부터 시작됐다.

“any 타입 왜 이렇게 많나요?”

any가 뭐가 문제인데

처음엔 이해가 안 갔다. 돌아가는데 뭐가 문제야.

TypeScript 배울 때 강의에서 들었다. “타입을 지정하세요.” 근데 any도 타입 아닌가. 에러도 안 나는데.

const handleSubmit = (data: any) => {
  // 이게 뭐가 문제지?
}

이렇게 쓰면 편했다. 빨간 줄도 안 생기고. 일단 돌아가니까.

김민수 선배 코멘트를 하나씩 읽었다.

“여기 data 타입 명시 필요합니다.” “any 쓰면 TypeScript 쓰는 의미가 없어요.” “FormData 타입 만들어서 쓰세요.”

FormData? 그게 뭔데.

또 다른 코멘트.

“response도 any네요. 이것도 인터페이스 만드세요.”

인터페이스. 들어본 적은 있다. 근데 어떻게 만드는지 모른다.

변명의 시간

일단 답장을 써야 했다.

“아… 네네 수정하겠습니다.”

손이 떨렸다. 뭘 어떻게 고쳐야 할지 모르겠는데.

5분 뒤에 선배가 또 슬랙을 보냈다.

“신입님, 잠깐 통화 가능하세요?”

끝났다. 진짜 혼나는 거다.

구글 미트 켰다. 선배 얼굴이 나왔다. 화난 것 같지는 않았다. 그게 더 무서웠다.

“신입님, any 타입 많이 쓰셨네요.”

“아… 네… 그게…”

변명이 나왔다. “타입을 어떻게 지정해야 할지 몰라서요. 에러가 계속 나서…”

선배가 한숨을 쉬었다. 조용한 한숨. 실망한 한숨.

“TypeScript는 타입을 지정하라고 쓰는 건데, any 쓰면 JavaScript랑 다를 게 없어요.”

알았다. 머리로는.

“근데 제가 타입을 몰라서…”

“그럼 공부하셔야죠.”

할 말이 없었다.

선배의 수업

선배가 화면 공유를 시작했다. 내 코드가 떴다.

const handleSubmit = (data: any) => {
  console.log(data);
  // ...
}

“이거부터 볼까요. data가 뭐가 들어올지 알죠?”

“네… 이메일이랑 비밀번호요.”

“그럼 이렇게 쓰면 돼요.”

interface FormData {
  email: string;
  password: string;
}

const handleSubmit = (data: FormData) => {
  console.log(data.email); // 자동완성도 됨
}

아. 이렇게 하는 거구나.

선배가 계속 설명했다. “이렇게 하면 타입 안전성이 생겨요. data.eamil 이렇게 오타 내면 에러 나죠.”

신기했다. 그동안 난 뭐 한 거지.

“response도 마찬가지예요.”

const response: any = await fetch('/api/login');

“이거 말고,”

interface LoginResponse {
  success: boolean;
  token?: string;
  error?: string;
}

const response: LoginResponse = await fetch('/api/login').then(r => r.json());

“이렇게 쓰면 response.token 자동완성도 되고, 오타도 방지되고.”

머리가 띵했다. 이렇게 쓰는 거였구나.

any를 쓴 이유

선배가 물었다. “근데 왜 any를 이렇게 많이 쓰신 거예요?”

솔직하게 말했다.

“타입 지정하려고 하면 빨간 줄이 너무 많이 생겨서요. 에러 메시지도 무슨 말인지 모르겠고.”

“‘Type string is not assignable to type number’ 이런 거요?”

“네… 그래서 일단 any 쓰면 없어지니까…”

선배가 웃었다. 비웃는 게 아니라 이해한다는 웃음이었다.

“저도 처음엔 그랬어요. any 쓰면 편하죠. 근데 나중에 더 힘들어져요.”

“어떻게요?”

“코드가 커지면, any 때문에 어디서 에러 날지 모르거든요. 런타임에 터져요.”

런타임. 그게 뭐지. 모르는 척했다. 일단 고개만 끄덕였다.

“그리고 any 쓰면 자동완성도 안 돼요. 매번 뭐가 들어있는지 확인해야 하고.”

아. 그래서 내가 맨날 console.log 찍었구나.

고치는 시간

통화 끝나고 코드를 다시 봤다. any가 17개였다.

하나씩 고쳤다. 선배가 알려준 대로.

첫 번째. FormData 인터페이스 만들기.

interface SignupFormData {
  email: string;
  password: string;
  name: string;
  age: number;
}

age는 number구나. 당연한데 몰랐다.

두 번째. API response 타입.

interface ApiResponse {
  success: boolean;
  data?: any; // 이것도 any네...
  error?: string;
}

data도 타입을 지정해야 했다. 뭐가 들어올까. 회원 정보니까.

interface UserData {
  id: number;
  email: string;
  name: string;
}

interface ApiResponse {
  success: boolean;
  data?: UserData;
  error?: string;
}

이렇게 하니까 깔끔했다. 뭔가 프로 같았다.

세 번째부터는 좀 빨라졌다. 패턴이 보였다.

  1. any 찾기
  2. 뭐가 들어오는지 생각하기
  3. 인터페이스 만들기
  4. 적용하기

2시간 걸렸다. any가 0개가 됐다.

PR 다시 올렸다. 코멘트 달았다.

“any 타입 모두 수정했습니다. 확인 부탁드립니다.”

다시 코드리뷰

30분 뒤에 선배가 답글 달았다.

“훨씬 좋아졌네요 👍”

심장이 두근거렸다. 칭찬이다. 선배한테 칭찬 들은 건 처음이다.

“몇 가지만 더 수정하면 될 것 같아요.”

또 코멘트가 달렸다. 3개. 아까보다 훨씬 적었다.

“여기 optional 타입 물음표 빼도 될 것 같아요.” “이 부분은 타입 추론 되니까 명시 안 해도 돼요.” “Union 타입 쓰면 더 정확할 것 같네요.”

Union 타입? 또 모르는 용어다.

검색했다. string | number 이런 거였다. 아 이런 것도 있구나.

고쳤다. 다시 올렸다.

10분 뒤에 Approve 떴다.

“LGTM! Merge 하셔도 됩니다.”

LGTM. Looks Good To Me. 이건 안다. 부트캠프에서 배웠다.

Merge 버튼 눌렀다. 초록색으로 바뀌었다. “Merged”.

기분이 이상했다. 뿌듯한데 부끄러웠다. 이제야 제대로 한 거 같은데, 진작 이렇게 했어야 했는데.

그날 저녁

퇴근하고 편의점 갔다. 맥주 2캔 샀다. 삼각김밥도.

원룸 들어와서 노트북 켰다. 인프런에 TypeScript 강의 검색했다.

“타입스크립트 올인원” 결제했다. 66000원. 아깝지만 필요했다.

첫 강의 재생했다. “TypeScript가 뭔가요?”

알 것 같으면서도 모르겠다. 제대로 배워본 적이 없으니까.

강의 들으면서 메모했다. 노션에.

  • any는 최대한 쓰지 말기
  • 인터페이스로 타입 정의하기
  • 타입 추론 활용하기
  • Union 타입 (|)
  • Optional 타입 (?)

메모하다가 든 생각. 나 지금까지 뭐 한 거지.

부트캠프 6개월. TypeScript 2주 배웠다. 근데 any만 썼다. 제대로 배운 게 없었다.

입사하고 8개월. 그동안 계속 any 썼다. 아무도 안 알려줬다. 물어보지도 않았다.

선배가 오늘 처음 짚어줬다. 그것도 코드리뷰에서. 부끄러웠다.

강의 1시간 들었다. 눈이 떠지는 기분이었다.

“타입은 문서예요. 코드를 설명하는.”

아. 그래서 타입을 정확히 써야 하는구나.

“any는 타입 시스템을 무너뜨려요.”

내가 한 짓이 그거였다. 타입 시스템을 무너뜨린 거.

다음날 아침

출근했다. 9시 8분. 오늘은 일찍 왔다.

슬랙 켰다. 김민수 선배가 메시지 보냈다.

“신입님, 어제 수정 잘 하셨어요. 앞으로도 타입 꼼꼼히 지정하시면 좋을 것 같아요.”

“네 감사합니다. TypeScript 강의 결제했습니다.”

“오 좋네요. 궁금한 거 있으면 언제든 물어보세요.”

물어봐도 되는구나. 몰랐다. 항상 혼자 끙끙댔는데.

오늘 작업 시작했다. 새로운 기능 추가. 장바구니 API 연결.

이번엔 달랐다. 먼저 타입부터 정의했다.

interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

interface CartResponse {
  items: CartItem[];
  total: number;
}

코드 쓰기 전에 타입 먼저. 선배가 알려준 대로.

신기했다. 자동완성이 됐다. item. 찍으니까 id, name, price, quantity가 뜬다.

console.log 안 찍어도 됐다. 뭐가 들어있는지 알았으니까.

에러도 빨리 찾았다. item.priceitem.cost로 잘못 썼는데 바로 빨간 줄 떴다.

“Property ‘cost’ does not exist on type ‘CartItem’.”

아. 이게 TypeScript구나.

2주 후

PR 올렸다. 장바구니 기능 완성. any는 0개.

김민수 선배가 리뷰했다. 코멘트 2개.

“타입 정의 깔끔하네요.” “LGTM!”

2개. 처음으로 한 자릿수 코멘트.

Approve 바로 떨어졌다. Merge 했다.

슬랙에 선배가 또 메시지 보냈다.

“신입님 많이 늘었어요. 타입 쓰는 거 보니까.”

기분이 좋았다. 진짜 좋았다.

인정받는 기분. 선배한테 혼난 지 2주 만에.

그날 저녁에 부트캠프 동기 단톡방에 썼다.

“너네 TypeScript any 쓰지 마. 진짜로.”

“왜? 편한데?”

“나 그거 때문에 혼났어. 제대로 배워.”

“ㅋㅋㅋ 오케이.”

얘들도 언젠가 혼날 거다. 나처럼.


any는 마약이었다. 당장 편하지만 나중에 독이 됐다. 2주 동안 TypeScript 강의 10시간 들었다. 이제야 TypeScript 쓰는 이유를 알았다. 선배한테 혼나서 다행이다. 안 그랬으면 계속 any 썼을 거다.