- 다양한 주제에 대해 자유롭게 글을 작성하는 게시판입니다.
Date | 21/02/26 23:42:04 |
Name | ikuk |
Link #1 | https://google.github.io/styleguide/tsguide.html |
Link #2 | https://namu.wiki/w/TypeScript |
Subject | 인터페이스냐 타입이냐 그것이 문제로다 |
어제 회사 기술팀 교류회에서 나눈 이야기를 짧게 공유드리려고 합니다. 토론도 되면 좋겠습니다. 코딩... 좋아하세요? 저는 무척 좋아합니다. 하지만 모르는 사람도 즐겁게 읽을 수 있는 글을 쓰고 싶습니다. 그러니 혹시 읽어보셨는데 모르겠다하는 부분이 있다면 꼭 알려주세요. 다음글을 쓸때 꼭 참고하겠습니다. 이 글은 변수가 무엇인지, 객체가 무엇인지 잘은 몰라도 들어는 본적 있는 분들에게 권해드립니다. 물론 아닌 분들도 이해할 수 있게 써보겠습니다... 저희 팀은 타입스크립트라고 하는 자바스크립트의 슈퍼셋(추가기능이 덕지덕지 붙은 확장팩)을 언어를 주고 쓰고 있습니다. 왜 이런 확장팩을 쓰냐고 묻는다면, 여러가지로 대답할 수 있겠지만... 가장 간단하게는 원체 느슨한 JS 위에 이것 저것 제한과 강력한 선언문을 넣어, 명확하게 코드를 쓸 수 있기 때문입니다. (가끔 JS를 똥으로 비유하곤 하는데, TS는 탈이 났을 때 지사제를 먹는것과 같은 효과가 납니다. 더 단단해지죠.) 거두절미하고 어제의 예시를 보여드리며 타입스크립트 코드를 쪼금 보여드리겠습니다.
위의 예시는 타입스크립트의 가장 기초적인 사용 예시로, 1-4줄은 User라는 타입을 새로 만든 것입니다. 문자열(String) 타입의 이름(firstName)과 성(lastName)을 가진 객체 유저(User)를 정의했습니다. 이렇게 하면 성이 '1'인 사람이거나 '@'인 사람은 유저가 될 수 없겠죠? 애초에 그런 사람은 없으리라 가정하는 겁니다. 6번째 줄부터 조금 재미있어지는데요, const users라고하는 변수를 선언한 뒤, 콜론으로 해당 변수의 타입을 User[]로 강제했습니다. 이 때 마지막 []는 배열(Array)을 뜻하는데요, 유저라는 객체를 여러개를 넣을 수 있는 배열로 선언된 겁니다. 이는 즉 users 변수에는 반드시 User 타입에 맞는 객체만이 배열로 n개 만큼 저장될 수 있음을 뜻합니다. thisBringsUsers()는 임의의 함수로, 유저를 두명 리턴해준다고 가정해봅시다. 이를 console.log(users)로 프린트 해보면 이렇게 나옵니다:
users라고 하는 변수의 내용물을 까보니 thisBringsUsers()가 돌려준 두명의 유저가 저장되어 있네요. (이 위지윅 에디터는 code의 결과물이 잘 안보이네요. 나중에 잘보이도록 css가 추가됐으면 좋겠습니다.) 이 단순한 코드의 의의는 무엇일까요? 먼저 타입을 선언했고, 그 타입을 배열로 받는 변수를 선언한 뒤에 함수를 호출했습니다. 이는 지금은 너무 잘돌아갈지 모르겠지만, 누군가가 유지보수를 하면서 정의가 바꾼다면 어떻게 될까요? 예를 들어서 그 사람의 성별이 필요해져서, 젠더(gender)라는 타입을 추가하게 됐습니다. JS는 느슨한 똥이기 때문에 아무런 문제없이 실행이 됩니다. 누군가 문제를 해결하기 전까지는 그대로 추가된 코드가 그냥 같이 주르륵 흐를 것입니다. 심지어 젠더가 기록된지 없는 사람은 그냥 없는대로 같이 실행될 겁니다.
이런 코드는 당장은 별 문제가 없을 것 같지만, 문제 없이 돌아가는 코드 속에 숨겨진 예외상황이 나비 효과를 부르게 됩니다. 성별에 따라 보여줘야 하는 메시지도 다를 것이고, 여러 기능들이 변할 텐데 이런 상황을 암묵적으로 코딩하는 문제가 생깁니다. 돌아는 갑니다만, '윤리'적인 문제가 발생합니다. 즉 유지보수가 지옥이 된다는 이야기 입니다. TS는 다릅니다. 지사제가 빡하고 힘을 주기 때문에 실행도 되지않고 바로 컴파일 에러를 띄워 코드에 문제가 있음을 알립니다. '당신이 선언한 타입과 다른 데이터가 들어왔으니, 문제가 있소'라고 하며 맞춤법을 틀린 것 마냥 붉은 밑줄을 그어버립니다. 1. 타입을 고치지 않았다면, 전혀 다른 타입의 데이터가 돌아왔습니다라고 에러를 고지할 것이고 2. thisBringsUsers()를 고치지 않았다면, 리턴받은 데이터가 User 타입과 다르다라고 알려줄 것입니다. 여기서 살짝 TS 초중급 이야기를 끼워넣어보겠습니다. gender에 true를 넣은 신입개발자 스미스씨는, male과 female 두개니까 1과 0으로 정의해서 문자열보다 메모리를 절약하는 코드를 써봤습니다. 그리고 사수는 정수리에 춉을 날리며 한 마디를 날립니다. 성별 밝히지 않음 옵션이 없으면 미국법 위반인데 어떡할래? 스미스씨가 당황하자, 사수는 enum을 알려줍니다.
TS는 enum이라고 하는 enumarate 타입을 선언할 수 있습니다, 한국어로 하면 '열거형'정도 되겠네요. 이렇게 되면 각각의 성별 앞에 목차 번호가 달리게 되는데요, Gender에 'male'값이 들어와도 숫자 0이 저장된것이나 다름없는 상태가 됩니다. 메모리 절약도 되고, 각 숫자가 뭔 의미인지 따로 적어둘 필요도 없어졌습니다. 심지어 나중에 안드로이드와 같은 인조인격이 들어오더라도 친절하게 gender에 android 한줄만 넣으면 대응이 가능해집니다. 유지보수가 엄청나게 쉬워집니다. 이런 사소한 정보 하나하나가 '정합성', 즉 모순이 없는 상태를 유지시키는 기본이되고 이런 윤리의식이 코드를 깨끗히 관리할 수 있게 해줍니다. TS를 좋아하는 사람들은 이런 과거의 많은 언어가 가지고 있던 장점들을 JS에서 편리하게 사용할 수 있기 때문이라 저는 생각합니다. 이걸로 해피엔딩이면 좋겠지만, 세상 속 개발에는 수많은 난제들이 존재합니다. 그중 하나가 오늘 이야기할 '객체란 무엇인가?' 입니다. 구글은 수만명의 개발자가 업무하는 곳이다보니, 여러 난제와 코딩 윤리에 대해 답변을 많이 준비하는 곳으로 유명합니다. 룰이 있으면 고민을 덜어내고 업무 효율이 올라가기 마련입니다. 그래서 정답에 가까운 스타일가이드를 만들고, 그에 맞추어 코딩을 요구합니다. TS는 마이크로소프트가 만든 JS슈퍼셋이지만, 이미 표준에 가깝게 자리잡은 개발언어가 되었기에 구글도 당연히 씁니다. 그리고 아래의 링크가 구글이 정의한 TS 스타일 가이드입니다. 오늘의 예제가 들어 있죠. https://google.github.io/styleguide/tsguide.html#interfaces-vs-type-aliases TS에는 인터페이스와 타입이라고 하는 두 가지 강제적 선언이 있습니다.
결국 둘다 타입 검사를 강제한다는 점에서 거의 똑같은 용도로 사용됩니다. 막간을 빌어 개발 구루분들께 첨언드리자면: Java나 C#과 같은 언어를 다루시는 분들은 인터페이스에 익숙하시니 이 설명에 갸우뚱하시겠지만, 프로토타입형 언어인 JS에게 타입이나 인터페이스는 결국 타입과 동일한 구조로 동작합니다. TS의 인터페이스와 타입을 어떻게 비유하면 좋을까 고민을 해봤습니다만, 인터페이스가 USB라면, 타입은 마치 메모리 소켓과 같다고 느꼈습니다. 즉 interface가 조금 더 느슨하게 움직입니다. 그 예를 보여드리자면:
위와 같이 interface를 두번 선언하면 User라는 인터페이스 하나로 세가지 타입이 병합됩니다. 마치 USB에 USB젠더를 연결하듯이 주욱 주욱 연결해나갈 수 있으며, 각 타입에 여러가지 옵션도 넣을 수 있습니다. readonly를 넣으면 데이터를 수정할 수 없게 되기 때문에, 성을 바꿀 수 없게 되며 ?를 넣어서 해당 파라미터가 없어도 객체 검사에 문제가 되지 않게끔 예외처리를 할 수도 있습니다. 타입은 병합이 안됩니다. 동일한 이름을 두번 쓰면 에러가 뜹니다. 하지만 기능적으로 안되는건 또 아닙니다. 유니언 타입이라는게 있거든요. 중1때 배운 집합 연산자가 바로 그것입니다. 복수의 타입을 이용해 위의 인터페이스와 엇비슷하게 만들수가 있습니다.
합집합을 의미하는 & 기호를 쓰면 복수의 타입을 동시에 쓸수 있습니다. 이것 외에도 인터페이스와 타입 각자 장점들이 많지만... 핵심을 가장 첫 머리에 말씀드렸던 '데이터 타입'을 감시하는 것이 주 목적인 것이고 이 것을 해결하는데 둘다 부족함은 없습니다. 그럼 언제 인터페이스를 쓰고, 언제 타입을 쓰느냐를 정하고 운용해야하는데 물론 클래스를 정의하는 과정에서 인터페이스를 뼈대로 사용하는 타언어의 기법을 그대로 빌려 용도에 따라 분리하는 것도 좋은 대안입니다만... 개운치가 않죠. 그리고 항상 예외는 추가됩니다. 암묵적 룰이 늘어나죠. 여기서 저희들의 명~하게 만든 한 구절을 번역해드리겠습니다. 구글은 이렇게 말합니다: 하지만 객체를 위한 타입 선언이 필요할 때에는 type 선언보다는 인터페이스를 사용하십시오. 왜냐구요? 이 두가지 방법은 거의 대동소이하기때문에, 코드가 다양해지지 않도록 하나를 통일하는 양자택일의 원론적인 입장에서 하나를 골라야 했습니다. 추가적으로, 몇가지 흥미로운 기술적 이유들이 있습니다. 이 페이지에서는 저희 타입스크립트 팀 리더의 말을 빌리고 싶습니다: 솔직하게 나는 모델화될 수 있는 모든 곳에는 전부 인터페이스를 쓸 거야. 타입 선언을 쓰는 건 별로 메리트가 없고, 아직 타입 선언에 출력과 퍼포먼스적으로 이슈가 있으니까. 끗. 위 글을 읽고 저희는 그냥 허탈하게 웃었습니다 ㅋㅋㅋ 저희도 비슷한 고민을 하던 차였거든요. 가장 큰 이유는 그냥 하나를 골라야 되니 고른거고, 아직은 타입선언의 퍼포먼스적 이슈가 있으니 인터페이스를 선호한다는 건데, 이는 퍼포먼스 이슈가 없으면 타입을 써도 된단 이야기이고 모델화할 수 있는 모든 곳에 인터페이스를 사용하겠다는 것 역시 취향에 가까운 선언이지만... 거꾸로 질문을 낳죠. 모델화란 무엇인가. 모델화 가능한 객체란 무엇인가... 여기서 모델은 개발의 Conceltual model의 약자로 개념적 모델, 즉 객체나 시스템 한 존재에 대한 개념적 정의를 뜻합니다. 말이 어렵습니다만... 위의 유저가 모델화를 거쳐 성, 이름, 성별, 나이 등의 필요한 정보를 담는 그릇이 되는 겁니다. 모델이 될 수 있는 모든 곳에 인터페이스를 넣는 다는 것은 대단히 객체지향적인 프로그래밍(Object oriented Programming)을 선호한다는 것인데 위에서 말했듯, 타언어의 기법이 훌륭하게 동작하니 우리는 이를 사용하겠다는 팀 리더의 발언이라고 생각하시면됩니다. 저 분이 틀렸다거나 하는게 전혀 아닙니다. 아마 수만명이 바라봐야하는 스타일가이드로서 명확하게 표현을 할 방법을 찾으신 걸지도 모르죠. 이 이야기를 더 풀면 어렵고 지지부진한 이야기만 늘어지니 결론은 여러분의 상상에 맡기겠습니다. 다시 핵심으로 돌아와서, 저희는 인터페이스와 타입 중 어느 방향으로 가닥을 잡을 지 고민을 했습니다. 저 팀리더의 말 때문에 결국 우리가 만들고 있는 서비스를 뒤돌아보게 됐습니다. 어디까지 모델화하고 객체로 운용해야하는가, 그럴 필요가 있는가 없는가. 비용은 얼마나 드는가. 메리트는 무엇인가. 그럼 결국 취향에 가까운 대답이 나오게 됩니다. 이게 읽기 편한데, 이게 가시적으로 좋은데. 이거 퍼포먼스나 출력이슈 우리는 없는데. 좀 더 알아봐야 할까요? 아 그럼 다음주까지 조금더 조사해보죠. 오픈된 결말로 끝나고, 다같이 인터페이스를 타입으로 변환시켜서 퍼포먼스 테스트를 하는 실험용 환경을 준비하기로 했습니다. 아마 다음주 중엔 결말이 나겠죠. 그렇게 개운한 결말은 아닙니다만, 이런 고민 하나하나는 결국 조직의 좋고 나쁜 성격을 수면 위로 들어내고, 조직을 대표하는 제품에 반영되게 됩니다. 결국은 비용문제로 손을 안대버린다던지, 아니면 퍼포먼스 이슈를 섬세하게 관리하지 않게 된다던지 하는 작은 나비효과들이 제품의 평가를 사소하게, 혹은 치명적으로 뒤틀어버리죠. 죄송합니다. 그리고 안타깝게도, 이제 그 것은 되돌이킬 수 없습니다. -Ryan Dahl Node.js의 창시자로 유명한 개발자 라이언 달은 2018년 6월 JSConf에서 'Node.js를 만들고 후회한 10가지'라는 발표를 했는데요. 여러 불필요하고 잘못 설계된 부분을 소개하며, 위와 같은 명언을 남겼습니다. (사진에서는 알 수 없지만, 청중들은 아주 크게 웃고 있습니다.) 후회하지 않는 개발을 하기 위해서 여러가지 난제에 맞서 싸워야 하는 상황이 있습니다만, 어제 오늘 같은 담론을 나눌 수 있는 개발자들이 여럿 있는 조직에서 근무한다는 건 제법 운이 좋은 일 같습니다. 아무리 새로 고친다고 해도, python2가 없던 일이 될 수 없기에 3으로 넘어가는데 10년이 넘는 시간이 걸렸고 스위치의 조이콘이 기능적 불량이 아무리 있어도, 2017년 3월 3일 초판된 이후 모델 체인지급의 개선은 어려운 것입니다. 이런 조바심이 개발자 커뮤니티의 여러 모습을 만드는 근원이 된다고 생각합니다. 가끔은 필요 이상으로 날카롭고 시니컬하지만, 결국은 뾰루퉁해도 다 같이 모여서 모래성을 짓는 기분이죠. 언젠가는 무너질 걸 알면서요. 결국 다 뒤집어 엎을 일이지만 아마 다음주까지 고민해야 할겁니다. 인터페이스냐 타입이냐... 3
이 게시판에 등록된 ikuk님의 최근 게시물
|