sw마에스트로 14기를 준비하면서, 합격 후기들을 이곳저곳 긁어모으면서 많은 도움을 얻었다. 나도 면접과정을 다시 돌아보면서 부족했던 부분을 점검하고, 15기 + 를 준비하시는 분들에게 도움이 됐으면 좋겠다는 생각에 후기글을..써보기로 했다


2차 코테 복기 및 주저리주저리 ↓

https://jofestudio.tistory.com/98

 

[SW 마에스트로 14기] 코딩테스트 2차.. 주저리 주저리

3월 8일 오늘 14시, 저번 주 토요일에 본 sw마에스트로 2차 코테의 결과가 나왔다. 일단 결과는 합격이어서 다음 주 심층면접을 준비해야 하는 상황. 마음도 진정시키고, 어차피 심층면접 때 2차 코

jofestudio.tistory.com


포트폴리오 준비과정

자신이 했던 프로젝트를 2개까지 쓸 수 있고, 만약 프로젝트 경험이 없다면 sw역량을 기르기 위해 했던 노력들이나 소마에서 만들고 싶은 프로젝트, 그리고 소마에서의 각오 등을 포트폴리오에 자유롭게 기술하라고 제시해주셨다.

 

사실 나는 포트폴리오가 걱정됐던 게

 

  • 백엔드를 현재 지향하지만 백 쪽은 공부한지 별로 안 됐고 백 관련 프로젝트 경험 x (물론 멋사 9기 해커톤할 때 django써보긴 했지만 안 쓴지 오래 지났고 지금은 spring쪽 공부하니까 논외..)
  • 프론트 쪽으로 했던 팀 플젝이 1개 있지만 대중적인 커뮤니티 만드는 플젝이어서 그닥 매력이 없음
  • 멋사 9기 해커톤 때 했던 팀 플젝은 너무 허접해서(ㅋㅋㅋㅋ..) 얘도 매력없음

 

이런 상황이었다.. 백 쪽으로 준비하고 있던 상황인데 백 관련 경험이 없다는 것도 내 약점이라 생각했고, 그렇다고 프론트 쪽에서의 경험을 어필하자니 그 경험도 의미있던 경험은 아니라고 생각될 여지가 있다는 것이 내 입장이었다.

 

전체적인 포폴의 컨셉 즉 방향을 어떻게 해주지부터 생각했다. 일단 난 2년 반 넘는 시간동안 네이버를 거쳐 티스토리까지 개발 관련 블로그를 나름 꾸준하게 운영해오고 있다. 또한 코딩에 관련된 컨텐츠를 올리는 유튜브(물론 영상들을 많이 올리진 않고 중간중간 코딩이 아닌 영상도 있지만)도 운영하고 있다. 내 강점, 나만의 차별점이 될 수 있는 부분들을 어필해야 할 것 같다고 생각해 일단 블로그&유튜브 운영에 관한 얘기는 해주자고 생각했다.

 

뭐가 됐든 플젝경험이 빈약한 편인 만큼, 활동내역 쪽에서 내가 어떤 경험들을 했는지를 어필해주는 게 좋다고 생각했다. 2년 전에 했던 부스트코스 서포터즈 3기, 대학생 코딩 캠프 1기를 비롯해 멋사 9기에서 활동했던 걸 적기로 했다. 사실 부스트코스 서포터즈 3기와 대학생 코딩 캠프 1기는 뭐 엄청난 의미와 배움이 있다거나 밖에서 알아주는(?) 그런 활동은 아니었지만, 일단 싹싹 긁어모아서 나 이런 저런 것들 해봤다!라고 어필해주는 게 내 상황에선 더 좋다고 생각돼서 넣어줬다. 그리고 작년 여름방학 때 학교에서 Level DB라는 오픈소스를 분석하는 스터디를 했던 것도 넣어줬다. 스터디할 때 조교님이 "시스템 소프트웨어 분야로 진출하지 않으시는 분들도 이 스터디를 포폴 경험에 넣으실 수 있을 겁니다~"라고 해줬던 적이 있는데, 당시에는 별 생각 없었지만 이걸 진짜로 포폴에 넣게 될 줄은 몰랐다.. 그래도 나한테는 의미가 있던 활동이었기 때문에 넣어줬다. 그 후론 마지막으로 작년 하반기에 우테코 5기 프리코스에 참가했던 걸 적어줬다. 최종 탈락하긴 했지만 그래도 열심히 참가한 과정이었고 그 안에서 얻은 게 많았으니까.

 

이렇게 일단 긁어모아서 활동내역을 적고 나니, "나 그래도 나름 이것저것 해봤네?"라는 생각이 들었다. 나름 그래도 이것저것 해보고 싶은 것들 해보고 성장하려고 했었구나..라는 생각. 그러다 이 부분을 어필하면 되겠다는 생각이 들었다. 성장하기 위해서 이런저런 경험들을 쌓아보려고 했다!라는 부분! 웹 프론트, 시스템 소프트웨어, 백엔드라는 분야들을 접해봤다는 것을 이 활동내역을 통해서 보여줄 수 있기 때문이었다. 그래 그러면 "나는 성장하려고 노력하는 사람이고, 다양한 경험들을 쌓으며 성장하려고 노력해왔다. 그리고 소마를 통해서도 성장하고 싶다!"라는 컨셉을 잡기로 했고, 이 컨셉을 포폴에 녹여내려 애썼다.

 

이제 플젝 2개를 어떤 것들을 보여주냐가 남았다. 프론트쪽 담당해서 커뮤니티 만들어본거는 그닥 큰 매력이 없어도 일단 넣어주는 게 좋을 것 같다고 생각했다. 이제 남은 하나를 뭘 채워야하나..를 고민했는데, 멋사 9기 해커톤에서 했던 허접한 플젝을 넣을까 하다가 작년에 학교에서 ai수업을 진행하며 했던 팀 프로젝트를 넣기로 했다. 사실 ai프로젝트는 앵간하면 안 넣으려고 했던 이유가 해당 플젝에서 진행했던 부분들에 대한 것들은 당시에 빡세게 했던 만큼 빠삭하지만, 그 외에는 나도 ai쪽은 정말 모르기 때문에 관련된 질문이 들어오면 탈탈 털릴 수도 있다는 걱정 때문이었다. 그러나 내가 포폴 컨셉 자체를 다양한 경험들을 접해보려고 했다는 점을 어필하는 쪽으로 정한 만큼, ai플젝을 했던 걸 보여주며 ai분야도 접해보려 했다는 것을 보여주는게 좀 더 포폴의 결에 맞는다는 생각이 들었다. 뭐 실제로 내가 ai쪽을 접하려고 그 수업을 신청했었으니까. 대신, 이 플젝에 관련된 질문들에 대한 대비는 그만큼 빡세게 해야 한다는 각오를 했다. 그래도 ai쪽 플젝을 보여주는게 뭔가 겉보기에도 까리해보인다는 생각도 했던 듯 하다.

 

 

포폴 발표 연습

포트폴리오를 만들면서 중간중간에 대본 작성도 계속 해나갔다. 3분이 생각보다 길 줄 알았는데 엄청 타이트했다. 사실 내가 어필하기로 했던 부분들은 포폴에만 녹여내는게 아니라 발표때도 말하는 게 좋다고 생각했는데, 그러기엔 3분을 훌쩍 넘어버린다.. 그래서 최대한 각종 활동들과 포폴과 관련된 내용들은 시작한 동기와 느낀 점 위주로만 설명하기로 했다.

 

발표 연습은 틈틈이 했다. 틈날 때마다 방에서 연습했고, 학교 가는 길에도 했고, 샤워하면서도 했고.. 면접 보기 3일전부터는 학교에서 저녁에 빈 강의실에서 빔 프로젝터로 화면을 띄우고 발표하는 연습을 했다.

 

 

면접 질문 대비

나는 포폴 자체를 나름대로 질문들을 유도하는 식으로 만들었다. (ex: 소프트 스킬을 기르기 위해 노력해오고 있습니다 → 면접관이 "소프트 스킬을 기르기 위해 어떤 노력들을 했죠?"라고 물을 수 있도록)..그래서 내 포폴을 보면서 면접관 입장에서 무러볼 만한 것들을 최대한 다 뽑아내서 준비했다. 포폴에서 나올 수 있는 기술적인 질문들은 꼬리질문들까지 최대한 준비해갔다 (ex: React를 공부해보셨는데 React의 특징이 뭐죠 ? 방금 Virtual DOM을 말씀해주셨는데 Virtual DOM이 뭐죠?, Class Activation Map이 어떤 기술이죠 ? → Class Activation Map의 원리가 뭐죠? → CNN이 어떤 구조를 가지고 있죠 ? → Pooling이 어떤 걸 말하는 거죠 ? → Pooling의 종류가 뭐가 있죠 등등..)

 

 

이 외에도 이전 기수에서 나왔던 인성질문(특히 포폴 발표가 생긴 13기 때 나온 기출 질문들을 중점적으로)들을 긁어모아서 따로 준비했고, cs질문은 기초적인 내용들(프로세스와 스레드 차이, osi 7계층, http vs https 등..) 20개 정도만 긁어서 준비했다.

 

아 그리고 2차 합격자들만 남아있는 오픈톡방에서 따로 면접 스터디를 구했다. 강남에서 모이는 스터디, 그리고 내 동네 쪽에서 모이는 스터디에 참가하면서 서로 3분동안 포폴을 발표하고 각자 포폴에서 질문할 수 있겠는 내용들을 질문하는 스터디였다. 다른 사람들 앞에서 발표하는 것도 연습할 수 있고, 다른 사람들은 내 포폴을 보고 이런 것들을 궁금해하면서 물을 수 있음을 알게 되니까 예상질문들에 대한 대비도 어느 정도 할 수 있었다. 만약 15기 +를 준비하시는 분들이 있다면 이런 면접 스터디에 참가해보는 것도 추천합니다:)

 

 

나름대로의 면접 전 마인드세팅

면접 질문들에 대한 답변을 말하는 연습을 계속하면서, 내가 말을 좀 빨리 하게 된다는 걸 알게 돼서 최대한 천천히 말하려고 연습했었다. 아무래도 긴장하게 되면 말을 좀 빨리하면서 더듬는 버릇이 있어서 그런 듯 하다. 근데 이게 단기간 내에 몰라볼 정도로 고쳐지기는 어렵다고 생각했다. 그래서 최대한 천천히 말하려 하되, 더듬어도 괜찮아! 절어도 괜찮아! 라는 걸 스스로한테 주입시켰다. 모든 질문에 대한 답을 다 잘 말할 필요 없다, 몇 개는 잘 모른다고 말해도 괜찮고 대답을 이상하게 해도 괜찮다.. 내가 준비한 내용들은 그래도 다 대답할 수 있게끔 하자면서 마인드 세팅을 해나갔다. 준비하지 못한 질문이 분명 들어올 수 있지만, 내 임기응변 능력이 그렇게 후달리지는 않을 거다라는 생각도 한 것 같다.

 

 

면접 복기..

도착하고나서 내가 배정받은 번호가 1번인걸 알게 됐다. 당연히 1번이 처음 발표하는 사람이겠군이란 생각에 긴장됐지만, 미리 1번인 걸 알아서 다행이라고 생각했다. 처음으로 발표 시작하게 될거라는 생각으로 심호흡을 좀 했다. 최대한 대기실에서 "내가 제일 잘 한다. 다 찍어누르겠다(?)"라는 걸 스스로에게 세뇌할려고 했다. 어떻게든 자신감을 채우고 싶었던 듯. 마음 한구석에서는 살짝 쫄리긴 했지만, 그래도 겉으로 보이는 건 안 쫀 척 당당해보으려고 어깨 계속 펴주면서 으르렁댔다. ㅋㅋㅋㅋㅋ..

 

역시나 처음으로 포폴 발표를 하게 됐다. 연습을 많이 해서 그런지 막상 발표할 때는 엄청 긴장되고 그러진 않았다. 그냥 적당한 긴장감 속에서 진행됐다. 연습 때 보여줬던 베스트 퍼포먼스만큼 발표한 건 아니지만 그래도 만족할 정도로 발표한 듯 해서 다행이었다. 이후엔 최대한 허리를 펴고 앉으면서 두 손을 무릎에 두고 있었다. 다른 분들 포폴 발표하는 거 볼 때는 고개를 완전 90도로 오른쪽으로 꺾어야 해서 힘들었음.

 

 

질문들은 다음과 같은 질문들을 받았다.

 

공통질문

1. 코테 보면서 어떤 점이 가장 어려웠는지, 그런 부분들을 해결하기 위해 뭘 해오고 있는지

🧐 아무래도 경험이 부족하다보니 문제를 보고 어떤 유형인지를 파악하는게 어려웠다고 했고, 다양한 문제를 접하면서 문제를 딱 봤을 때 어떤 유형인지 알 수 있게끔 하려고 한다고 답했다

 

2. 자소서에서 각자가 만들기로 했던 서비스 한 번 더 설명해주고 비즈니스 모델은 어떤 거 생각햇는지

🧐 그냥 구상했던 서비스 다시 말씀드리면서 ~~기능 만들려고 한다고 답했다.. 부끄럽지만 솔직히 비즈니스 모델이 뭔지 잘 몰라서 최대한 자연스럽게(?) 비즈니스 모델에 대한 설명은 하지 않았는데 티가 났을 것 같다ㅋㅋㅋㅋ..근데 끝나고 비즈니스 모델이 뭔지에 대해 검색해봤는데, 다른사람들도 다 비즈니스 모델에 대한 답을 하진 않은 것 같아 다행이었다

 

3. 소마에서는 팀플을 하기 위해서 팀을 구해야 하는데, 본인의 어떤 점을 채워줄 수 있는 팀원을 원하며 그 팀원을 어떻게 설득할 건지

🧐 백엔드를 하고 싶은데 백엔드 관련된 경험은 부족하다보니 백엔드를 잘하는 사람과 팀을 하고 싶다고 했고, “나는 백엔드 경험은 부족해도 프론트랑 ai쪽 해봤다. 팀플에 잘 녹아들 수 있다”라고 설득해보겠다고 대답했다.

 

4. 소마가 널 왜 뽑아햐 하는지 (소마에 넌 뭘 줄 수 있는지..)

🧐 처음엔 질문 뉘앙스가 너희가 뭘 기여해줄 수 있냐를 묻는 줄 알았는데 마지막에는 뽑아야 하는 이유를 말해달라길래....준비했던 내용(나는 소마를 통해 성장하고 싶은 욕구가 정말 강하기 때문에 날 뽑아야 한다)를 말했다. 왜냐면 왜 너를 뽑아야하냐는 질문은 내가 준비를 했던 내용이었기 떄문! 근데 답하고 보니 뭔가 분위기가 띠용?이 되는 것 같았다. 사실 냉정하게 생각해보면 어떤 부분을 기여할 수 있는지를 말해달란거였는데, 이 부분은 준비를 못한 부분이라 당황해서 그런지 “왜 뽑아야 하는지”라는 질문에만 꽂혀서 그에 대해 준비했던 것만 말한 거였다.. 그래서 면접관님이 어떤 부분을 기여해줄 수 있는지에 대해서 되물으셔서 열정으로 분위기를 밝게 해보겠다는? 그런 답을 했다. 스스로도 부족한 답임을 알고 있어서 살짝 침울했지만, 다른 분들도 어떤 부분을 기여하겠다기보다는 스스로 성장하고 싶다는 점을 어필하셔서 다행이었다.

 

개인질문

1. ai관련 진행했던 플젝 간단하게 다시 설명해달라.

🧐 CAM이란 기술을 활용해 모델이 얼굴을 보고 감정을 분류할 때 얼굴의 어느 부분을 보고 분류하는지를 시각적으로 파악해보는 플젝이었다고 답했다.

 

2. CAM과 전이학습을 어떻게 활용한 거냐. 왜 전이학습 사용했냐.

🧐 처음에는 CAM과 전이학습이 서로 연관이 있는 거냐는 뉘앙스로 물어보셔서 서로 연관이 없다고 대답했는데, 거기에 대한 자세한 설명을 해달라면서 전이학습을 왜 했는지 물어보신 걸로 기억한다. 확보한 이미지 양이 적어서 새로운 모델을 만들어 학습시키기엔 효율이 안 좋을 것 같아 전이학습을 사용했다고 했고, 그 과정에서 전이학습을 위해 들고 온 모델에 CAM을 적용했다고 말했다.

 

 

3. 그럼 전이학습할 때 모델은 어떤 거 사용했고 어떤 식으로 학습시켰냐

🧐 VGG16 모델을 사용했고, 이 모델이 기존에 학습했던 이미지의 종류와 우리 플젝에서 새로 학습시키려는 이미지의 종류가 다르고 확보한 이미지가 적어서 모델에 새로 붙인 classifier layer뿐만 아니라 모델이 원래 가지고 있던 레이어들도 함께 학습시켜주는 식으로 했다고 말했다.

 

 

4. 결과를 잘 도출했고, 그래서 감정을 분류하는데 있어 모델이 중요시하는 부분이 어느 부분인지 볼 수 있게 된 것 같다. 도출된 결과는 어떤 특징이 있었냐

🧐 happy는 입 쪽을 보고 분류한다는 점을 볼 수 있었고 angry로 분류되는 애들은 코쪽, 주름쪽을 보고 분류한다는 점을 알 수 있었다고 답함.

 

그리고 공통적으로 마지막으로 하고 싶은 말들을 시켰다. 거기에 대해선

🧐 “소마 과정이 쉽게 잡을 수 있는게 아닌 소중한 기회라고 생각하고 있고, 소마를 통해 얻을 수 있는게 정말 많다고 생각한다. 처음 접하는 ai에 관련된 플젝을 할 때도, Level DB라는 규모있는 오픈소스를 분석할 때도, 우테코에서 처음 접하는 자바를 사용하며 미션을 수행할 때도 여러운 것들이 많앗지만 포기하기 않고 끝까지 해내왔다. 소마도 결코 쉽지 않겠지만 포기하지 않고 끝까지 해내서 꼭 스스로가 성장할 수 있는 기회로 반드시 만들어보겠다” 라고 답했다

 

 

음..포폴 발표할 때는 말을 그닥 빨리 하지 않았던 것 같은데 결국 질문같은 걸 받을 때 말을 빨리 하고 중간에는 말을 좀 더듬기도 한 것 같아 아쉬웠다. 이 부분은 진짜 고쳐나가야 할 듯 하다. 말을 왜 자꾸 빨리 하게 되는지..ㅜㅠ

 

일단 면접과정에 대한 후회는 없다. AI관련된 개인질문들은 바로바로 대답을 이어나갔기 때문에 꽤나 잘 대답했다고 생각하고, 꼬리질문을 이어가주시던 면접관 분도 시간이 있으면 더 물어보고 싶지만 그래도 플젝을 잘 진행해본 것 같다라는 뉘앙스로 말해주셔서 만족했다.

 

그 외 공통질문들에 대한 대답도 그 때의 내가 답할 수 있는 최선을 다한 대답이었다고 생각해서 후회가 들진 않는다(아 이렇게 답할걸.. 이런 생각 안 듦). 내가 준비했던 내용들에 대해선 다 답한 것 같아 과정에 대한 후회는 정말 남지 않을 것 같다.

 

다만 아쉬운 건, 다른 사람들한테는 질문들이 좀 많았는데(특히 블록체인 기술로 플젝한 분) 나한테는 그만큼의 질문이 들어오지 않았다는 거다. 나 진짜 내 포폴에 관련된 내용들, 포폴에 작성된 기술 관련된 내용들은 다 방어할 수 있을 정도로 꼼꼼히 준비했는데.. 나한테도 그 분만큼의 꼬리질문을 해주면 다 답해줄 수 있는데..  나 준비 많이 했다는거 보여줄 수 있는데.. 그만큼의 질문이 들어오지 않은 것에 대한 아쉬움. 준비한 것들을 다 못 보여준 느낌..이었다.

 

그래도 후회는 없다. 들어온 질문들에 대해서는 내가 할 수 있는 최대치 내에서 답변했다고 생각했다.

 

 

다른 분에게 들어온 개인질문 중 기억나는 것들

- 블록체인의 Finality가 뭐고 왜 중요한가요?

- 본인이 작성한 플젝에서 하신 역할이 뭔가요?

- OAuth를 사용해봤다고 했는데 OAuth가 뭔가요?

- 블록체인과 NFT의 차이점에 대해 말해주세요

 

우리 분과는 단순 프론트 / 백 프로젝트를 하신 분들에겐 그 플젝에서 다뤄봤다고 기재한 기술들에 대한 질문(ex: OAuth를 써봤다고 하는데 그게 뭔가요 등)을 하셨던 반면, AI나 블록체인같은 기술이 가미된 프로젝트를 한 분들에게는 그 기술들에 대한 질문들을 꼬리 질문 형식으로 계속 이어나가셨다. 그래서 단순 프론트/백 플젝을 하신 분들에겐 솔직히 질문이 별로 안 갔다. 물론 나에게도 질문이 많이 온 건 아닌 것 같다.. 아무래도 면접관님들 입장에서도 매력적으로 느껴지는 플젝은 AI나 블록체인을 활용한 플젝이기 때문일 것 같다. 그래서 어떻게보면 내가 AI플젝을 포폴에 넣었고, 그거에 대한 대비를 최대한 꼼꼼히 하려고 했어서 다행이라는 생각도 들었다.


그리고 합격..!

3월 23일 5시 경에 메일이 왔다.

 

 

일단 기쁘다. 마에스트로 과정을 옛날부터 한 4학년쯤 되면 해봐야지~ 했는데 정말 하게 돼서 너무 좋다. 글을 쓰고 있는 지금도 실감이 안 날 정도로 너무 좋다.

그리고 내가 공부해왔던 것들이 아무 의미 없던 게 아니었음을, 나 그래도 잘 하고 있었음을 인정받은 것 같아서 더욱 더 좋은 것 같다. 눈에 보이는 성취를 이루고 싶다는 생각을 했는데, 소마에 이렇게 합격하게 돼서 너무 다행이고 좋다. 

 

퀄리티있는 플젝을 하며 폭발적으로 성장할 기회. 다른 사람들과의 네트워크를 만들 기회, 생활비 벌자고 20살때부터 알바하던 것들 다 때려치고 지원금받으면서 오로지 성장에만 몰입할 수 있는 기회. 소마를 통해 내가 바라던 것들에 대한 기회를 잡게 됐다. 하지만 아직 잡기만 했을 뿐. 성장하지도, 다른 사람과의 네트워크를 만들지도, 몰입하지도 않았다. 단순히 기회만 얻게 됐을 뿐. 앞으로의 1년이 정말 중요할 거고, 소마에서 어떻게 지내가는지에 따라 내가 원하던 것들을 잡게 될지 안 될지가 나뉠 것이다. 이제부터가 중요하다.

 

잘..해보자!

 

3월 8일 오늘 14시, 저번 주 토요일에 본 sw마에스트로 2차 코테의 결과가 나왔다.

일단 결과는 합격이어서 다음 주 심층면접을 준비해야 하는 상황.

마음도 진정시키고, 어차피 심층면접 때 2차 코테 내용을 물을 수도 있다 하니 간단히 복기할 겸 짧은 글을 써보기로 한다.


1차 코딩테스트 

  유형 난이도(백준 기준) 예상
1번 단순 구현 실버 4
2번 라인스위핑 or 완전탐색 골드 5 ~ 골드 4
3번 조합 + BFS 실버 1 ~ 골드 5
4번 DFS(백트래킹) 골드 5 ~ 골드 4
5번(sql) 문자열 및 정규표현식  

※ 유형은 각자가 푼 방법이 다르기 때문에 꼭 저 유형이 나왔다고 할 수 없습니다. 난이도는 제가 체감한 난이도입니다. 시험 응시 당시 제 티어는 골드 1이었습니다.

 

우선 나는 sql을 공부한 적이 없기에 1차 코테 전에 1주일 정도 프로그래머스 고득점 kit을 풀며 sql을 준비했었다. 그래서 코테 응시할 때 염두한 전략이 sql을 먼저 푸는 거였는데, 고득점 kit에서는 보지 못한 유형이 나와 못 풀었다..

 

1, 3번 풀고 2번을 풀다가 끝이 났다. 즉 2솔로 제출..이 때 서버가 터져서 45분의 추가시간이 주어졌는데, 나는 스터디룸을 예약하여 응시하던 상태라 추가시간을 활용하지 못 했다. 그러나 마에스트로 측에서도 이런 불합리성 등을 감안했는지 1차 코테를 응시한 전 인원을 합격처리해줬다.

 

2차 코딩테스트

  유형 난이도(백준 기준)
1번 단순 구현 실버 3 ~ 실버 2
2번 그리디..? 골드 3 이상
3번 자료구조 + 빡구현 골드 4
4번 플로이드 워셜..? 골드 3 이상
5번(sql) LEFT JOIN  

※ 유형은 각자가 푼 방법이 다르기 때문에 꼭 저 유형이 나왔다고 할 수 없습니다. 난이도는 제가 체감한 난이도입니다. 시험 응시 당시 제 티어는 골드 1이었습니다.

 

1, 3, 5번 풀어서 냈는데, 5번 풀 때는 그냥 JOIN으로 했다가 다 끝나고 나서 LEFT JOIN으로 했어야 한다는 걸 알아버렸다. 1, 3번은 구현유형이었던 만큼 내 풀이에 대한 확신을 가진 상태. sql문제가 부분점수가 있는지는 모르겠지만 2솔 ~ 2.5솔 정도 된다고 생각하고 기다렸다. 아 그리고 2번 문제는 풀다가 끝났었다.

 

 

암튼, 결과는 합격. 기준은 모른다. 1.5솔 합격이신 분도 계시고, 2솔 탈락인 분도 계시고..

 

 

이제부턴 정말 주저리.

 

개인적으로 소마 자체에 합격하는 것을 바라지만, 코테 합격이라도 했으면 좋겠다는 생각을 했다.

 

 작년 12월에 우아한테크코스 프리코스에서 떨어진 적이 있다. 최종테스트에서 떨어진 것도 아니고, 최종테스트 인원 선발 과정에서 떨어졌었다. 적잖은 충격을 받았었다. 학교 과제까지 제껴두고 프리코스 과정에 올인했었고, 제시받은 모든 요구사항들을 잘 지켜나갔으며 매 주차마다 회고글을 작성하고 다른 사람들과 피어리뷰도 진행하는 등 내가 할 수 있는 모든 최선을 다해 프리코스에 임했던 만큼, 최소한 최종테스트를 응시할 기회는 받을 수 있으리라 예상했기 때문이다.

 어찌보면 내 자만이었을 것 같기도 하다. 왜냐하면 난 지금까지 내 스스로가 최선을 다했다고 느꼈던 부분들, 내가 봐도 나 정말 열심히 했다고 느꼈던 부분들에서는 비교적 좋은 결과를 얻어왔기 때문인 것 같다. 그러나 최종테스트 응시를 할 기회조차 주어지지 않았다. 답답한 것은, 어떤 부분이 부족해서 떨어졌는지를 모른다는 거였다.

 그래서였을까. 스스로에게 회의감이 들었다. 내가 해온 공부방식이 사실은 잘못된 방식이 아닐까라는 느낌. 매번 달리지는 않았어도 꾸준히 조금씩이라도 걸어가고 있다고 생각한 길이 사실 돌아보면 이미 한참 전에 잘못 들어선 길이 아닐까. 나는 제대로, 잘 해오고 있던 걸까. 내가 공부해왔던 것들이 사실 무의미했던 것들이 아닐까.

 

 사실 이번 마에스트로에서 응시한 코테가 내 인생 첫 코딩테스트다. 1월 말부터 시작해 약 1달 정도 강도있게 준비하기 시작했지만, 사실 이전에도 알고리즘 공부를 했던 적이 있다. 빡세게 하던 건 아니었지만 20년도 7월부터 21년도 2월 정도까지 기초적인 문제들을 풀었었고, 21년도 9월부터 22년도 4월정도까지는 멋사 동아리에서 만난 사람들과 스터디 형태로 일주일에 4문제씩은 풀었었다. 1월 말에 다시 알고리즘 공부를 시작한 시점에 이전에 공부했던 것들을 까먹기도 했고 감도 잃은 상태였지만, 어찌됐든 난 알고리즘 공부를 옛날부터 해왔던 사람인 거다.

 그래서 조금은 무서웠다. 만약 이번에 마에스트로 과정을 코테 단계에서 떨어진다면, 우아한테크코스 프리코스 과정에서 탈락했을 때와 마찬가지로 뭔가 스스로가 부정당할 것 같은 느낌이었다. "나 그래도 옛날부터 조금씩은 알고리즘 공부해온 사람인데, 사실 내가 옛날부터 해왔던 알고리즘 공부 역시 잘못된 게 아닐까" 라는 두려움.

 

 물론 내가 이번 코테를 잘 본 사람은 아니다. 난 딱 합격컷 위에 서있는 사람일거다. 그래도 다행인 건, 내가 틀리지 않았다는 느낌이 든다는 거다. 거의 다 까먹은 상태에서 1달동안 빡세게 준비하긴 했지만, 그 전에 알고리즘 공부를 했던 적이 없다면 아마 코테에서 떨어졌을 거다. 그 이전에 알고리즘 공부를 한 경험들이 있기 때문에 1달동안 준비가 나름대로 돼서 붙은 것 같다. 내가 공부해온 것들이 의미가 있었다 라는 걸 느껴서 스스로에게 정말 다행인 것 같다.

 

 암튼, 다음 주가 심층면접인 만큼 다시 한 번 최선을 다해 준비해야한다. 마지막 한 걸음 남았다고 생각한다. 열심히 준비해서 최선을 다해보자. 화이팅..!

 

 

 

 

 어느덧 마지막 주차다. 3주 전 긴장 반 설렘 반으로 프리코스를 시작하던 때가 엊그제 같은데 벌써 마지막 주차 미션이라니..

 

 3주차 미션을 다시금 돌아보면 부셔지고 깨지는 순간들이었다. 고통스러웠던 시간이었다고 해도 될 것 같다. 그러나 포비 코치님이 코수타에서 고통 속에서 학습이 이뤄진다고 말씀해주셨다. 나중에 이런 고통들을 이겨내고 뒤를 돌아보면 이 산을 많이 올라왔음을 내려다볼 수 있을 때가 있을 거다. 목표로 가는 길이 쉽고 평탄하지 많은 않고 당연히 가시밭길도 있을 거다. 예방주사 한 번 맞았다고 생각하고 다시금 재정비할 때다.

 3주차 미션을 진행하며 고민했던 요소들이 공통 피드백에 녹아있었다. 대표적으론 private메서드를 어떻게 테스트할지?와 같은 내용이 있었고 테스트하기 쉬운 구조로 코드를 짜는 팁도 있었다. 저번 미션에 어설프게 TDD쓰려 하느니 미션의 요구사항과 목표에만 집중하자!라는 생각으로 기능 구현이 다 끝날 때까지 테스트 코드를 1개도 짜지 않았었다..덕분에 프로덕션 코드를 테스트하려 할 때 이걸 어떻게 테스트해야하지..하면서 고민하다가 접근지정자를 전부 다 public으로 바꿔야 했던 뼈아픈 실책을 내기도 했던 만큼, 이번 피드백 역시 피가 되고 살이 되고 뼈가 되는 내용들이었다. 

 

 어느덧 4주차, 프리코스의 마지막 미션.  마무리 매듭을 짓는 단계를, 즉 끝을 어설프게 끝내고 싶진 않다. 3주차에 대한 조금의 아쉬움이 있는 만큼, 마지막 4주차만큼은 스스로가 만족할 수 있는 완벽한 미션진행을 하고 싶다. 3주차를 통해 예방주사를 맞았겠다, 후회없이 모든 걸 쏟아내고 싶다. 모든 걸 갈아넣고 최선을 다 해야 후회가 안 남고, 최종선발여부를 떠나 프리코스가 끝나고 뒤를 돌아봤을 때 내가 산을 올라왔음을, 성장했음을 느낄 수 있을 것이다.  


저번 주에 인사를 나눴던 MVC와 친해지기로 하다

완벽하게 하고 싶단 욕심이 들었던 만큼, 우선 전달받은 공통 피드백을 보며 내가 신경쓰지 못한 부분들과 좀 더 신경써야 할 부분들을 보기로 했다. 그리고 요구사항에 명시된 제한들을 파악하고 먼저 정리해보기로 했다.

 

  • "변경을 못 하게 막으려는 경우 final키워드 붙이기"
  • "객체는 객체스럽게 쓰기"
  • "인스턴스 변수 개수 줄이기"

 

  • "메서드 라인길이 10라인 안 넘도록 구현"
  • "파라미터 3개로 제한"

 

등등.. 이걸 어떻게 해? 라는 수준의 경악스러운 조건은 없었다고 생각했다. 오히려 좀 더 빡세진 만큼 내가 더 잘하면 된다! 보여주면 된다! show and prove하면 된다! 라는 생각을 했고, 더 어려워진 만큼 완벽하게 해냈을 때 스스로 느낄 수 있는 만족감과 가치가 더욱 더 커져서 좋다는 생각이 들었다.

 

 암튼 그러면서 어떻게 만들지 계속 생각해봤다. 이번 미션에서의 유별난 특징 중 하나는 InputView, OutputView등의 클래스를 제시해주며 특정 기능들은 어느 클래스에서만 구현하라는 조건들이 많이 있었다는 점이었다.

 

"음..? 클래스 이름이 View.. 입력은 무조건 InputView에서만 한다..?"

"결과 출력하는 역할을 OutputView가 하게 한다..?"

"BridgeGame클래스는 InputView와 OutputView를 사용할 수 없다..?

 

..이거 어디서 많이 보던 문장인데..?

설마..

이거, MVC를 써보란거 아니야 ..

 

 물론 MVC패턴을 쓰란 말이 직접적으로 있던 것도 아니고, 특히나 저번 주에는 "내가 잘 모르는 것들(TDD, MVC같은..)에 집중하기보다는 요구사항을 완벽히 지키는 것에 초점을 맞추자!"라는 생각을 하긴 했다. 근데 제시된 내용에 따르면 단순 클래스 분리가 아닌 역할에 따른 분리를 요구하고 있고, 이런 분리에 알맞는 놈은 내가 잘은 몰라도 저번 주에 나랑 가볍게 인사한 사이인 MVC가 있다는 생각이 들었다.

 

 "그래, MVC 이 참에 배워서 적용해보자!"

 

물론 제일 첫번째가 되야 하는 것은 요구사항들과 기능들을 명확히 지키는 것. 그러나 이 정도의 제한이 없던 3주차와는 달리 MVC를 배워서 도입하는게 오히려 그런 요구사항과 기능을 지키는데 도움이 되리라 판단했다. 구현을 시작해야 한다는 마음을 내려놓고, 달리기를 시작하기 전 몸풀기가 먼저다라는 생각으로 MVC를 구글링하며 공부했다. 그리고 유튜브 테코톡으로 올라온 MVC영상들을 보며, MVC가 뭔지 그리고 어떻게 적용할지 구상을 하고 구현단계로 들어갔다.


어떻게 만들어볼까?

 뭐 잘 모르긴 하지만, 일단 view의 역할은 InputView와 OutputView가 한다. 그리고 컨트롤러를 둬서 모델과 뷰 사이에서 이런저런 고생을 시켜야 한다! 모델에는 뷰나 컨트롤러의 코드가 들어있으면 안 된다!! 등등을 숙지한 상태. 작성한 기능목록을 토대로 어떤 클래스들이 이 기능들을 수행하게 할까를 고민한 다음 이들을 적절히 model, controller, view로 나누기로 했다.

 

초기 클래스 구상 모습. 물론 실제로 다 구현하고 돌아보니 초기에 생각해둔 것과 많이 달랐다

 

 이 단계에서 가장 고민했던 건 "검증기능을 어디로 부여할 것인가"였다. 예외처리 기능이 있어야 하기 때문에 Validate만 따로 하는 클래스도 필요하다고 생각했지만, 저번 주 로또게임을 하면서 우테코가 제시한 Lotto클래스에 검증기능이 있던 걸 봤었기 때문에 검증기능을 따로 클래스를 만들어빼낼지, 아니면 클래스들 내부로 넣을지를 많이 고민했다. 이번 미션이 가지는 기존 미션과의 또 다른 차이점 중 하나가 입력으로부터 예외가 발생하면 메시지를 출력하고 종료하는게 아니라 그 페이즈부터 다시 입력을 받게 하는 것이었기 때문에, 입력한 값을 검증하고 처리하는 것도 도메인 로직 중 하나로 생각했었다. 이를 위해서 클래스 내부로 검증기능을 넣어야 할 것 같다곤 생각했는데, 문제는 다리 길이를 입력받는 부분이었다. 위 아래 칸 선택 입력과 게임커맨드 입력(재시작/종료)는 문자열 그대로 받으니까 상관없지만 다리 길이는 정수로 바꿔서 받아야했다. 즉 정수가 입력된 게 아니라면 바꿔서 받는 과정에서 오류가 날 것이고, 이를 막으려면 Bridge클래스 외부에서 문자열 여부를 검증해야 한다..! 

 

 즉 검증기능을 도메인 로직으로 생각해서 이를 클래스 내부로  넣어야 할 것 같지만, 다리 길이의 경우 외부에서 1차 검증(문자열이냐 아니냐)를 마쳐야 하기 때문에 깔끔하지 않다고 생각했다(검증을 두 곳에서 나눠서 하는 꼴이 되므로..). 차라리 아예 클래스 외부에서 검증기능을 하는게 더 깔끔하지 않나라는 생각이 들었다. 그러나 숫자로 들어온 다리 길이를 검증하는 것은 도메인 로직으로 보는게 맞지만 문자로 들어온 다리 길이를 검증하는 것은 기대하는 상황이 아니므로 도메인 로직이 아닌 것으로 봐야 한다는 생각이 들었기 때문에, 일단 좀 찝찝하지만 다리 길이만 그렇게 검증을 분리해서 진행하는 쪽으로 방향을 잡았다.


만들고, 점검하고, 고치고!

 저번 주 내가 했던 뼈아픈 실수를 복기해보자면..잘 모르는 TDD같은 거 어설프게 하려 하지 말고, 요구사항 지키는 데에 집중하자! 라는 생각을 가진 건 좋았다. 요구사항 지키기도 빡센데 제시되지 않은 영역까지 다 가져가려 하는건 되려 독이 될 수 있다고 생각했기 때문이다. 그러나 문제는, 그렇다곤 하지만 제시된 기능 조건들이 모두 동작하는 코드들이 완성될 때까지 테스트 코드를 하나도 만들지 않았다는 것이었다. 

 

내가 왜 그랬을까..ㅠ

 

그래서 그런지, 테스트 코드를 작성하는 과정에서 어려움을 겪을 수밖에 없었다. 왜냐하면 테스트를 고려하면서 만들어나가지 않았기 때문! 중간중간 테스트 코드를 작성했다면 프로덕션 코드의 리팩터링을 거치면서 진행했었겠지만, 이미 덩치가 커질대로 커진 상태에서 할려고 했던 게 문제였다. 결국에 private를 전부다 public으로 풀어서 진행했다는..

 

 암튼 이번엔 그런 뼈아픈 역사를 다시 한 번 저지르지 않고자 각 기능묶음별로 구현이 끝나면 테스트 코드를 작성해 잘 동작하는지 테스트한 다음, 구현한 프로덕션 코드들을 리팩터링하는 식으로 진행했다. 이 과정에서 프로덕션 코드가 좀 더 테스트하기 쉬운 구조로 만들어져 가는 것도 느꼈고, 리팩터링이 끝난 후에도 만들어둔 테스트 코드들을 딸깍 클릭하는 것으로 구현한 기능들이 그대로 동작하는지 확인할 수 있어 정말 편했다. 

 

 이런 과정에서 가장 좋았던 점은 테스트의 강력함을 계속해서 느낄 수 있었다는 거다. 일단 첫 번째로, 테스트 코드를 작성하며 내가 미처 신경쓰지 못한 예외상황을 마주할 수 있었다.

 

 

이건 내가 작성한 Bridge클래스에 대한 테스트 코드로, 플레이어가 다리를 다 건넜는지 확인하는 도메인 로직에 대한 테스트를 진행한다. U, D, D형태 다리를 건너다가 마지막에 U로 건너가서 떨어지는 상황을 연출했으니, 플레이어의 도착여부는 False가 될 것이다.

 

그러나 위 테스트는 통과하지 않았었다. 즉, 플레이어의 도착여부가 true로 돼있었다. 즉 내가 짠 프로덕션 코드에 문제가 생겼던 것! 당시에 내가 짰던 isArrived메서드는 다음과 같았다.

 

 

바로 플레이어의 현재 위치와 다리의 길이와 같다면, 즉 플레이어가 다리 끝에 있다면 거기서 살아있던 죽어있던 true를 리턴하도록 코드가 짜여있던 거다. 그러나 플레이어가 다리 끝까지 갔어도 그 마지막 칸에서 죽었다면, 도착한 게 아니여야 한다. 나는 이 부분을 놓쳤던 거다. 내가 짠 프로덕션 코드에서 내가 신경 쓰지 못한 부분들을 알게 해줬다. 만약 테스트를 안 했다면..나는 이런 오류가 있는지도 모르고 지나쳤을 수도 있었다.

 

 두 번째로 느낀 강력함은 테스트 코드도 리팩토링이 가능하다는 것. 피어리뷰를 하다가 ParameterizedTest를 통해 여러 값들에 대한 테스트를 편하게 할 수 있음을 알게 됐고(참고로 이 내용은 3주차가 끝나고 제공된 공통 피드백에도 있었다), 이를 통해 비슷한 케이스에 대한 여러 개의 테스트 코드를 짜둔 것을 보기 좋게 리팩터링해갈 수 있었다. 그리고 오류가 나는 상황은 isInstanceOf로 확인할 수 있던 것과는 달리 오류가 나지 않는 상황은 "오류가 안 난다"라는 걸 테스트하는 방법을 몰라서 메서드의 리턴값이 확인되는지 정도로만 테스트했었는데, doesNotThrowAnyException을 통해 오류가 안 나는 상황을 테스트할 수도 있음을 알게 되기도 했다. 정말이지 테스트 도구가 제공해주는 강력한 편의성을 느낄 수 있었다..저번 미션들에선 사용자가 입력하는 과정 자체를 하나의 테스트로 만들 수 있다는 점에서도 그 편의성에 놀랐지만, 테스트 작성 자체에서도 상당한 편의성을 제공해준다니..이렇게 테스트 코드들도 리팩터링을 하고 보니, 다른 사람이 볼 때 내가 어떤 것들을 테스트하는건지 쉽게 이해할 수 있게 만들 수 있겠다는 생각이 들었다. 덕분에 이전까지는 느끼지 못했던 문장인 "테스트 코드가 일종의 문서로 쓰일 수 있다"라는 것이 어떤 걸 의미하는지 조금은 이해할 수 있었다. 요구하는 기능이 어떻게 동작해야 하는지, 어떤 상황에서 어떤 값들이 쓰이느냐에 따라 오류가 날 수 있는지를 명시해줄 수 있기 때문인 거다. 단순히 테스트 코드는 내가 만든 기능이 잘 동작하는지 쉽게 파악할 수 있게 해줄 뿐만 아니라, 하나의 문서가 될 수 있다는 것. 사실 다른 것도 많겠지만 이번 프리코스에서의 최대 수확인 뭐니뭐니해도 테스트 코드가 아닐까. 라는 생각이 들 정도로 이 강력함에 매료되고 있는 것 같다. 얘는 정말 내가 어떤 걸 공부하던 계속 들고 가야할 동반자가 되야 할 것 같다.


딱 보니, 이 놈만 고치면 되겠구먼

 이번 주 미션은 MVC에 최대한 입각한 방법으로 (물론 고수들이 보면 어설픈 구조일 수 있지만..) 클래스 분리를 하고자 했다. 물론 처음에도 어느 정도 클래스들을 어떻게 분리할지 생각해둔 상태로 구현을 시작했지만, 구현 중간마다 필요성을 느끼면 좀 더 세분화해서 클래스들을 쪼개나갔다. 또한 model, view, controller 외에도 service라는 역할을 하는 애들을 둘 수도 있음을 알게 되고, 전역적으로 쓰이는 애들을 util 안에 둬서 관리할 있다는 것 등을 알게 된 후엔 최대한 계층을 생각해서 수정을 해나갔다.

 

 이 방식의 장점은, 역시나 리팩터링할 때 피부로 느낄 수 있었다. 바로 "고치기 쉽다"는 것! 특정 기능을 고치거나 리팩터링을 할 땐 딱 그 놈이 있는 클래스만 고쳐주면 됐다. 예를 들어, BridgeGame에서 Player의 생사여부(떨어지면 죽는다는 식으로 구현함)를 확인하여 전달하는 코드가 있고, 만약 죽는 기능을 수정해야 하는 상황이라면 BridgeGame은 건드리지 않고 Player의 코드만 건드리면 됐던 거다. 사용하는 쪽(BridgeGame)은 건드리지 않고, 그 로직을 제공하는 놈(Player)만 건드리면 되도록 구조가 잡혔기 때문일 것이다. 즉 각각이 하는 역할에 따라 클래스들을 분리한 덕에 "난 네가 내부적으로 어찌 동작하는지는 모르지만, 그냥 너한테서 ~라는 정보만 얻으면 땡이야!" 라고 말하는 듯한 구조로 짜여진 거다.


4주차 종료 및 회고

 결국 그렇게, 4주차 미션까지 종료됐다. 뭔가 시원섭섭한 느낌. 6년전 이맘때쯤이었을까? 수능이 끝나고나서 느꼈던 감정이 조금은 다시 느껴졌던 것 같다. 뭔가 몰두해서 하던 것이 끝난 느낌. 

 

  떠올려봤다. 프리코스를 시작할 때, 그리고 우테코를 지원할 때의 내 마음가짐. 성장을 위해 후회없이 이 과정에 임하자고 다짐했던 각오. 난 정말 최선을 다 했을까? "최선을 다 해 임할 것이다"라고 말할 수는 있어도, "최선을 다 했다"라고 말을 꺼내는 것은 그 무게가 사뭇 다를 것이다.  학교 과제? 다 포기했다. 학교에서 받는 학점보다 우테코로 얻는 가치가 더 크다고 생각했으니까. 사실상 알바와 팀플을 제외하고 잠도 줄여가며 남는 시간을 대부분 프리코스 미션에 투자했다. 바쁜 날도 하루 최소 2시간씩은 투자했고, 알바를 갈 때도 노트북을 챙겨가서 주문이 뜸한 동안 틈틈이 미션을 하곤 했다.

 

그러나 정말 최선을 다 했다고 말할 수 있을까?

 

 부끄럽게도...물론 이렇게 한 것도 힘들었지만..고통스러웠지만, 더욱 더 쥐어짤 수 있지 않았을까라는 생각이 든다. 좀 더 짜낼걸..이란 아쉬움이 조금은 남는다. 최선을 다 했다는 말을 스스로에게 해주며 토닥거리고 싶지만, 함부로 그럴 수 없을 것 같다. 더 할 수 있는 여지가 분명 있었다.

 

 뒤를 돌아봤다. 내 프리코스 목표였던 성장을 나는 이뤄냈을까? 내가 얻은 건 뭐가 있을까?

 

 우선 첫 번째. 몰입하는 경험을 쌓을 수 있었다. 사실 나는 프로그래밍 쪽으로는 깊은 몰입 경험이 있긴 있어도 많지 않다. 그러나 이번 우테코기간은 정말 그 미션들에 내가 몰입하고 있다는 걸 느끼는 순간들이 많았다. 버스를 타는 길에 창밖을 보며 "음 집가서 이런 식으로 만들어볼까.."하던 순간들도 있었고, 알바 출근길에는 클래스를 이렇게 바꾸는 게 더 나을 것 같다고 생각하던 순간들도 있었다. 잠자리에 누웠다가 번뜩하고 생각난 것들을 바로 적어두는 순간들 역시 있었다. 

 

자다가 퍼뜩 발상이 떠올라 카톡 내게보내기로 아이디어를 적곤 했다

 

 내가 프로그래밍을 해오면서 이런 몰입을 스스로 느껴본 적이 얼마나 있었을까? 많지 않다. 솔직히 드물다. 그러나 프리코스 과정을 통해 내가 몰입하고 있음을 느낄 수 있었다. 사실 내 단점 중 하나가 주의력이 좀 낮다는 것이라고 생각했는데, 이번 우테코를 통해 내가 뭔가에 집중하고 있다, 몰입하고 있다는 순간들을 느낄 수 있어 스스로에게 너무 놀라웠다. 역시 나도 하면 하는 놈이구나 라는 것도 느껴진 것 같다. 이런 귀중한 경험은 분명 쉽게 쌓을 수 있는 것이 아니다

 

 그리고 두 번째. 테스트 코드를 공부하게 됐다는 것. 뭐 길게 말할 필요 있을까? 사실상 이번 프리코스 최대 수확이라 해도 과언이 아니다. 이 맛있는 놈을 이제서야 알게 되다니..지금껏 이런 명품을 모르고 있던 게 부끄러울 지경. 

 

 마지막 세 번째. 내 목표에 한 발짝은 다가가지 않았을까.  내가 우테코를 지원한 궁극적인 이유는 "읽기 좋고 재사용성이 좋은 실전적 코드를 작성하는 개발자가 되고 싶어서"이다. 지금의 내겐 원하는 기능을 어떻게든 꾸역꾸역 구현할 수 있는 능력이 있다고 생각하지만, 코드를 고칠 때나 며칠이 지나고 내가 짠 코드를 볼 때 "이게 뭐하는 거지..어딜 고쳐야 하지.." 라는 생각에 현타가 많이 오곤 했었다.

 그러나 이번 프리코스 미션들을 통해, 자바 컨벤션도 찾아보고, 메서드 네이밍을 고민하고 메서드 분리를 연습하고, 10줄 제한을 통과하려고 낑낑대보기도 하고, 클래스 분리도 연습하고, MVC패턴에 입각한 분리도 연습해봤다. 물론 아직도 많이 부족하지만 각각의 연습들을 통해 내 코드가 읽기 좋은 코드가 되고 있음을 스스로가 느낄 수  있었다.

 뭐 별 거 아니라고 할 수 있다. 그게 뭐 어쨌다고? 그게 그렇게 좋았냐(?)고 할 수도 있고..ㅋㅋㅋㅋ. 그러나 지금까지 하나의 파일에서 대부분 코드를 작성하던 내게, 구조를 생각하지 않고 구현만 하던 내가 처음으로 나름대로 읽기 쉽고 재사용하기 쉬운 구조로 만들겠다고 낑낑대며 만든 프로그램들이다. 네이밍? 개인적으론 꽤나 맘에 든다. 팔은 안으로 굽는다고 내 코드들이 이렇게 예뻐보일수가 없다. 메서드 분리를 연습하고, 클래스 분리를 연습하고, MVC패턴 도입을 연습해보며 고치기 쉬운 구조가 만들어졌다고 스스로 느끼기도 했다. 학교 수업시간엔 교수님이 학생들에게 자신이 코드를 짜는 걸 보여줄 땐 "저렇게 짜면 다른 사람이 보기 불편할텐데..분리를 좀 해야할텐데.."등등의 생각이 들기도 했다. 읽기 좋고 재사용성이 좋은 코드를 짜야된다는 일종의 마인드셋을 조금은 장착한게 아닐까? 일단 원하는 기능이 동작하면 된다는 마인드로 주먹구구식 구현을 하던 과거의 나에 비하면 정말 유의미한 성장을 거뒀다고 생각한다.

 

 

 그럼 프리코스 진행기간 동안 내가 부족한 점은 뭐였을까?

 

 일단 뭐, 자바 문법을 잘 몰랐다는 것에 한 표 던진다. 이번 프리코스를 통해 자바를 처음 공부하게 됐으니 당연하다고도 생각하지만..자바의 stream이라든지, stringBuilder라든지 이런 놈들을 통해 내가 원하는 기능들을 좀 더 편하게 만들 수 있었다. 일단 모르는 게 생기면 그 부분만 공부한다는 마인드로 프리코스를 임하긴 했고, 나도 프론트를 공부하던 시절의 경험들을 통해 이런 학습방법이 좀 더 효율적이란 건 알고 있지만 역시 기본서 하나를 읽으며 어떤 문법들이 있는지 알아두면 좋을 것 같다. 프리코스를 시작하며 구매한 자바의 정석을 지금까진 필요한 부분들만 봤지만, 한 번 처음부터 끝까지 정독하며 문법을 좀 더 공부하고 제공하는 기능들이 뭐가 있는지도 살펴볼 필요가 있을 것이다.

 두 번째로, 도메인 등에 대한 확실한 개념을 잡아야 할 것이다. 대략적으로 어떠한 것이라는 것만 알지, 도메인을 아직도 정확히 뭐다라고 설명할 수 없고, 이에 대한 나만의 기준도 확실히 잡히지 않은 상태다. 업계에서 자주 쓰이는 용어인 만큼 이 개념을 좀 더 공부해야 할 필요가 있다.

  세 번째, 결국 읽기 좋고 결국은 재사용성이 좋은 코드를 짜는데 미숙하다. 물론 이 부분에 대한 성장을 목표로 우테코에 지원하긴 했고, 이 부분에 대한 성장을 느끼긴 했지만 아직 많이 부족하다. 솔직히 피어리뷰를 하며 다른 사람의 코드를 보면 정말 감탄할 때가 많았다. 이 방법을 어떻게 생각했을까 싶은 부분이 있기도 하고, 디자인 패턴 시간에 배우는 패턴 등을 접목해 만든 사람들도 보였다.. 그 사람들은 그만큼 뼈를 깎는 노력을 많이 해온 사람들이 아닐까. 나 역시 계속해서 이런 부분들을 의식적으로 연습해야 한다.

 

 

 

 길다면 길고 짧다면 짧은 4주였다. 솔직히 휙 지나간 듯 하고, 내가 최종선발테스트에 뽑힐지는 모르겠다. 그러나 이번 우테코를 통해 읽기 좋고 재사용성이 좋은 코드를 짜는 법에 대한 힌트를 얻을 수 있었고, 몰입하는 경험도 쌓을 수 있었다. 물론 아쉬운 점들이 있긴 했지만 그래도 내 목표에는 한 발짝은 다가선 것 같다. 그러나 아직 부족한 점이 많기에, 이 한 발짝에 만족하지 않고 더 큰 보폭으로 걸어가고 뛰어가고 싶다.  그게 우테코를 통했으면 좋겠지만.. 설사 최종테스트선발이 안 되더라도, 테스트를 보고 떨어지라도, 내 목표를 향해 달려나가자. 

3주차를 시작하며 - 앞으로는 <할 수 있는 부분>에 집중하기로

 어느덧 프리코스의 절반이 지났다. 2주차 미션이 끝나고 다른 사람들이 제출한 코드들과 회고들을 살펴봤다. 우테코가 선발 과정이 아니라 양성 과정임을 잘 알곤 있지만, MVC패턴이라든지 stream이라던지 내가 잘 모르는 것들을 도입해서 미션을 해결하는 사람들을 보며 조금은 주눅이 들었다. 열정있고 실력있는 사람들과 함께하는 환경에서 공부하고 싶은 맘에 우테코를 지원하기도 했었고, 저런 사람들을 보면서 자극도 받으면서 "자바를 이번에 시작해서 잘 모를 뿐 내가 공부하면 더 예쁘게 코드 짤 수 있어!"라는 나름의 의욕도 생겼지만, 한 편으로는 어찌됐든 나보다 더 실력있어보이는 이 사람들 사이에서 내가 선발될 수 있을까라는 불안감이 들었다.

 

 그러다가 2시에 코수타를 통해 포비 님의 조언을 듣게 됐다. 나처럼 주눅이 든(?) 사람들에게 해주는 말이었는데, 바로 "할 수 있는 것에 집중해서 해봐라!"라는 말이었다. (feat. TDD는 쓰레기다) 괜히 뭐 클린코드를 지향한다고 처음부터 이것저것 좋은 것들을 싹 다 할라고 하지 말고, 우테코 미션에서 요구하는 내용들에만 집중해서 구현하려고 해봐라! 라는 의미였다.

 돌이켜보면, 나 역시 1 ~ 2주차 미션들을 진행할 때 각 미션의 요구사항들 뿐만 아니라 그 외에 추가적으로 이것저것 시도할려고 했었던 것 같다. "있어 보이기 위해" 커밋메시지를 죄다 영어로 작성했고, 잘 할 줄도 모르는 TDD 한 번 해본다고 테스트 먼저 만들고 프로덕션 만들기를 어설프게 시도했었다. 그런데 이것들이 해당 주차에서 우테코가 요구하던 것들이었는가? 커밋메시지 컨벤션을 지키라는 말이 있었을 뿐 한글/영어에 대한 제한은 없었고, TDD를 사용해라 라는 말 역시 없었다. 우테코가 원하던 것은 코치님들이 메일로 명시했던 "함수 분리 연습 및 테스팅 도구에 익숙해지기"이지 영어로 뽀대나게 커밋메시지를 작성하는 것이나 TDD를 도입한 개발에 익숙해지기가 아닐 것이다. 물론 내 딴에는 "이번 기회를 통해 성장하기 위해서, 습관을 들이기 위해서"라는 명목으로 저런 것들을 시도할려고 했던 것이고, 그것들이 결코 안 좋은 일들은 아닐 것이다. 그러나 되려 왠지 모를 조바심에 내가 너무 많은 걸 할려고 했던 게 아닐까.

 생각해봤다. 내가 지금까지 프로그래밍을 공부하면서 함수 분리에 신경을 쓰려 했던 적이 있었나? 테스트란 걸 해볼려고 했던 적이 한 번이라도 있나? 이 질문들은 우테코의 2주차 미션의 목표였다. 함수 분리? 이렇게까지 한 함수가 하나의 기능만 하게 하려고 기를 쓰고 해본건 이번이 처음이다. 테스트? 테스트 코드 자체를 이번에 처음 해봤다. 그리고 이걸 통해서 "한 함수가 하나의 기능만 하게 만드는 걸 통해 좀 더 읽기 좋고 고치기 쉬운 코드가 만들어지는군! " 이란 걸 느낄 수 있었고, "내가 직접 프로그램 수행해서 입력해볼 필요 없이 클릭 한 번으로 테스트가 가능하다고? 너무 편하잖아! "도 느낄 수 있었다. 굳이 뭐 커밋메시지 영어로 쓰고 TDD할려고 하려고 하고 이런 거 필요없이, 우테코에서 제시한 목표를 이루려고 하는 과정에서 조금씩 성장한 내 모습을 볼 수 있었다.

 

 솔직히 말하면, 나는 우테코에서 제시한 목표들을 파고드는 것도 빡세다. 저번 2주차만 해도 내가 함수 분리에 얼마나 많은 시간과 고민을 투자했었는가. 그런데 내가 커밋메시지를 영어로 쓴다고 계속 papago번역기 돌리면서 얼마나 많은 시간을 "날렸는가". (뭐 내가 영어를 잘 못하기 때문인 것 같기도..)  사실 한글로 써도 되는 부분이다. 배보다 배꼽이 더 커지면 안된다. 미션의 목표를 달성하려는 과정에서 성장할 것이고, 거기에 집중해야 한다는 생각이 들었다. 그 외에 집중하는 게 더 많아지면 안 된다.

 

 MVC패턴, stream..다른 사람들의 코드엔 뭐 SOLID의 ocp원칙에 위배된다는 류의 피드백이 달리는 것도 봤다. 지금의 나에겐 다른 차원의 피드백으로 느껴진다. 솔직히 대단하다. 그 정도로 코드를 보는 안목이 있다는 건 그 사람의 실력이 그만큼 엄청나다는 반증이다. 그렇다고 해서, 내가 MVC같은 걸 꼭 써야 한다는 강박은 갖지 말자. 당장 이번 3주차 미션의 목표는 클래스 분리 연습과 도메인 로직에 대한 단위 테스트 작성 연습이다. 이 두 목표를 달성하려는 과정에서도 나는 성장할 거다. 왜? 저런 걸 안 해봤으니까. 그 과정에서 MVC를 도입하는게 더 좋을 것 같다 이런 생각이 들면 써도 되겠지만, 처음부터 "다들 MVC쓰는 거 같으니까 나도 해야 돼!! "라는 생각으로 잘 모르는 MVC를 어설프게 하려고 하지 말자.  그거야말로 배보다 배꼽이 커지는 상황일 것이다.

 

 지금의 내가 할 수 있는 것에 최대한 집중하자. 미션의 목표로 주어진 내용을 달성하려고 하는 것에 집중하자. 저번 2주차에서 느꼈듯이, 그 과정에서 분명히 성장할 것이다. 최대한 우테코가 제시하는 목표를 달성하려고 하다보면, 선발 여부에 관련없이 스스로 돌이켜봤을 때 전에 비해 많은 성장을 했을 것이고, 내가 나아간 방향도 올바른 방향일 것이다.


클래스 분리 및 도메인 로직 분리 - 오랜만에 마주한 벽

이번 주 학습 목표는 

  1. 클래스 분리 연습
  2. 도메인 로직에 대한 단위 테스트 연습

이다. 이 둘을 중심으로 파고들기로 맘먹고 클래스 분리부터 해보자고 생각했다.

 

 2주차 미션을 진행하며 야구게임기 / 검증기 / 출력기 라는 3개의 객체로 분리해 코드를 짜긴 했지만, 이것보다 좀 더 심화된 객체분리를 연습해보는 것이 미션 취지에 맞다고 생각했다. 역할에 따라 분리하면 되겠거니 했지만 감이 잘 잡히지 않았다. 해본 적이 없었기 때문이다..그러다가 2주차 피드백에 실려있던 강의영상을 떠올렸다. 제이슨 코치님이 기능 목록을 정의한 후 각 기능들을 어떤 클래스가 수행하게 할지 정하고 클래스를 만드셨는데, 이 방법을 내 기능 목록에도 적용시켜 보기로 한 것이다.

 이 방법을 통해 기능 목록의 각 기능들을 어떤 클래스가 수행하게 할지, 메서드 이름은 뭘로 할지 가닥을 잡을 수 있었나. 크게 입력기 클래스 / 출력기 클래스 / 검증기 클래스/ 생성기 클래스 등으로 역할에 따라 분류했는데, 머지 않아 안개에 가려져 잘 안 보였던 큰 산 하나에 도착하게 됐다. 바로

 

도메인 로직과 UI로직을 분리해 구현하기

이거 구글 쳐서 나오는거 가져왔는데 저작권 문제 걸리나요..그럼 삭제하겠습니다 ㅠ

 정말 그게 뭔데 그 잡채였다. 내가 아는 도메인은 url창에 관련된 그 놈밖에 없는데..분위기상 그걸 말하는 건 아닌 것 같았다. 저번 주에 테스트 코드를 접하면서 테스트 코드에 대해 공부했듯이, 이번에도 도메인 로직이나 UI로직이 뭘 말하는 건지부터 공부해야겠다는 생각이 들었고 구글링부터 시작했다.

 

근데 시간이 그리 많이 걸릴 줄 몰랐지..

 

 도메인 로직, 즉 비즈니스 로직이 뭔지 설명하는 글들은 제법 있었지만, "결국 추상적인 말이다"라는 느낌을 세게 받았다. UI로직이 사용자와 상호작용하는 로직, 즉 입출력에 관한 것을 말한다는 건 금방 이해됐지만 도미엔 로직 이 놈이 문제였다. 일단 도메인이란 "해결하고자 하는 문제 영역"이란 의미를 가진다고 한다. 그러면 도메인 로직은 "해결하고자 하는 문제 영역에 대한 로직 주어진 문제에 대한 해결을 이끌어내는 로직"이 된다. 

 처음에는 음~ 이었다. 5초 뒤 응? 이었다. 해결해고자 하는 문제 영역에 대한 로직이란 건 알겠는데, 그 범위를 어디까지 봐야 하는지를 모르겠다. 한 마디로 추상적인 개념이라고 느껴졌다. 유효성 검사 로직을 예로 들면, 유효성 검사 자체가 문제에 대한 해결은 아니니 도메인 로직으로 보면 안 된다고 생각할 수도 있지만 문제에 대한 해결을 이끌어내는데 필요하니 도메인 로직이라고 볼 수도 있다고 느껴졌다. 코에 붙이면 코걸이, 귀에 붙이면 귀걸이처럼 생각하는 관점에 따라 도메인 로직이라고도 아니라고도 부를 수 있는 것 같아 엄청난 혼란이 느껴졌다. 

 개인적으론..이런 추상적인 개념들은 어떻게든 이해를 하고 넘어가야 하는 성격이다. 애시당초에 이 개념에 대한 이해가 없으면 이번 미션 진행이 힘들기도 할 것이다. 이대로는 안된다는 생각이 들어 구글링을 계속 했다..

 

 그러나 답을 찾지 못 했다. 심지어는 도메인을 명사로 부르는 경우도 있는 것 같아 더욱 혼란스러웠다. 해결하고자 하는 문제 영역을 도메인이라 하는 것 같은데, ~~하는 것의 도메인은 OO, OO, OO입니다 이런 식으로 명사로 부르기도 하는 걸 봤다. 혼란은 더욱 가중됐다. 도대체 도메인이라는게 구체적으로 뭘 말하는건지? 도메인 로직은 그럼 구체적으로 뭘 말하는건지?

 

 여기서 끝이 아니었다. MVC패턴 같은 걸 남들 다 쓰니까 나도? 라는 생각으로 쓰지 말자고 다짐했건만, 도메인 로직을 알아보던 중 MVC패턴이 도메인 로직과 UI로직을 분리하기에 좋다는 말을 듣게 됐다. 나도 모르게 무의식적으로 MVC패턴을 써야한다는 생각이 스며들기 시작했다. 그러면 따로 도메인 패키지를 만들어서 클래스들을 넣어야 할 것 같은데, 어떤 파일들을 넣어야 하지? 라는 생각이 들었다. 아직 도메인이 뭔지도 정확히 잘 모르겠는데? 라는 상황이었고, 잘 모르고 써본 적도 없는 MVC패턴을 써야 한다고 생각하니 너무 답답했다.. 

 프로그래밍을 공부하고 얼마 지나지 않아 마주친 포인터의 벽. 알고리즘 공부를 시작하며 느꼈던 재귀의 벽. 그리고 이번에 또 하나의 벽을 마주하게 되는 느낌이었다. 내가 잘 모르는 개념들, 별로 해본 적 없는 구조를 주어진 시간 내에 이해하고 구현할 수 있을까? 라는 생각이 들기 시작했다.

 

 그러다 포비 코치님이 해주신 말씀이 떠올랐다. 1주차가 끝나고 코수타를 진행할 때 해주신 "기능 목록이나 메서드 분리의 답은 없다! 스스로가 자신의 기준을 만들어 나가야 한다!" 라는 말씀이었다. 결국에 추상적인 개념이라면, 스스로의 기준을 만들어 가야 하는 것이고 도메인 로직 역시 내가 나만의 기준을 만들어야 한다는 생각이 들었다. 이게 맞는지 모르겠지만, 물론 맞다 틀리다가 있는지도 모르겠지만, 나름대로 사용자와 상호작용하는 로직을 UI로직으로 분류하고 원하는 것을 도출하는 로직을 도메인 로직으로 분류하기로 일단 기준을 잡아보기로 했다. MVC패턴? 처음에 3주차 미션을 시작하며 했던 다짐인 "할 수 있는 것에 집중하기"를 다시 한 번 상기시켰다. 이번 주 목표인 클래스 분리 연습에 초점을 최대한 두고, 메서드 분리나 함수 네이밍같은 부분(2주차가 끝나고 피어리뷰를 할 때 네이밍을 잘 한 것 같다고 칭찬들을 해주셔서 메서드 네이밍만큼은 남들보다 잘 하고 싶다는 생각이 있기도 했다)을 좀 더 신경쓰자!!라고 생각하며 다시금 멘탈을 잡을 수 있었다.

 

 확실히 느낀 건, 메서드 분리보다 클래스 분리가 훨씬 더 어렵다. 한 메서드가 하나의 기능을 하도록 분리하는 것도 물론 쉽지 않지만, 클래스 분리는 메서드 분리보다 개인의 주관이 좀 더 많이 들어가는 문제라고 느껴졌기 때문이다. 대표적으로 이걸 느꼈던 건 "검증(validation)"이었다. 금액 입력에 대한 유효성 검사, 당첨 번호 및 보너스 번호 입력 시 유효성 검사가 필요해서 나는 이걸 검증기 클래스들을 따로 만들어 구현했었다. 왜냐면 내 클래스 분리의 기준은 "역할"이었으니까. 그러나 이번 미션에서는 Lotto클래스에서 validate메서드를 호출해 검증하는 작업을 해야 했었다.! validate메서드의 내부에서는 실제로 검증을 수행하는 코드가 들어있었다. 내 생각은 검증은 따로 클래스를 만들고 그 클래스를 통해 수행하는 게 맞는 것 같은데, 왜 로또 객체 내부적으로 검증을 수행하게끔 하는거지? 라는 생각이 들었다. "로또 번호 검증"에서 "검증"에 포커스를 두면 검증기 클래스에서 담당하는게 맞고, "로또"에 포커스를 두면 로또 클래스에서 담당하게 할 수도 있다는 생각이 들었다. ..이런 부분에서 개인의 주관이 좀 더 들어가는 문제라고 느껴진 것이다. 이것 역시 나만의 기준을 만들어야 한다는 생각이 들었고, 지금 당장은 역할에 따라 구분하는 게 좋다는 생각이 들었던만큼 검증기 클래스를 따로 만드는 식으로 진행했다. Lotto의 validate는 검증기 객체를 이용한 검증을 하는 식으로 바꾸고.

 

클래스 분리를 이번에 연습하며 느낀 장점? 일단 확실히 한 파일의 길이가 짧아진다. 그리고 나중에 기능을 수정할 땐 그 역할에 맞는 클래스만 수정하면 됐다. 근데 이런 장점보다도 솔직히 고통스러웠던 시간의 임팩트가 훨씬 길었다..고통이 장점으로 얻는 달콤함을 압도하는 느낌..


도메인 로직에 대한 단위 테스트 작성 - private vs public..이거 뭔가 잘못됐다..?

 저번 주 미션에서는 TDD를 한 번 해보겠다고 설치면서 테스트 코드를 먼저 만들고 프로덕션 코드를 나중에 만드는 식으로 진행했었다. 그러나 이번 주부터는 "할 수 있는 것에 집중하기"를 다짐한 만큼 어설픈 TDD를 잘 해보겠다고 깝치지 말고 우테코에서 제시하는 요구사항과 목표들에 포커스를 맞추기로 했다. 즉 테스트 코드 먼저 만들고 프로덕션 코드 만들기라는 방식을 쓰지 않았다.

 

근데 문제는, 그렇다고 내가 아예 테스트 코드를 모든 기능의 구현이 다 끝날 때까지 한 개도 만들지 않았다는 것..

 

"안 돼!! 멈춰!! 돌아가서 테스트 코드를 짜!!"

 

 모든 기능의 구현이 끝나고 나서야 테스트 코드의 작성을 시작했다. 1주차 미션을 통해서 기능 목록을 정의하는 나만의 기준을 세웠었는데, 그건 바로 목록의 각 기능들이 하나의 메서드 또는 코드에 대응하도록 만드는 것이었다. 2주차 때 이런 식으로 기능 목록을 세우는 걸 연습했던 만큼, 이번 3주차 역시 기능목록의 각 항목들이 하나의 메서드에 대응하는 구조였다. 즉 이 메서드들을 테스트 코드로 테스트하는 식으로만 만들면 그것이 단위 테스트가 될 것이다!

 

 라고 생각하고, 내가 생각한 도메인에 해당하는 각 클래스별로 테스트클래스를 만들면서 테스트코드들을 만들었지만..문제에 봉착했다. 테스트를 진행할 수 없었다. 왜냐하면..2주차 미션의 피어리뷰 당시 죄다 public으로 작성한 점을 지적받았어서, 이번엔 외부에서 호출할 수 있는 메서드말고 내부적으로 쓰는 메서드들은 죄다 private로 만들었기 때문이다.. 물론 다시 public으로 돌리면 될 일이다. 다 public으로 돌린다고 본 미션에서의 지장은 없다. 그러나 죄다 public으로 할 때 드러나는 단점이 너무나도 명확하지 않은가. 외부에서 함부로 다 쓸 수 있게 하는 건..좋지 않다. 내가 채택한 클래스 분리가 이런 결과를 도출해낸 건가? 다른 식으로 클래스 분리를 해야했던 걸까? 라는 생각들이 들었다. 그러나 시간이 충분하지 않았다. 클래스 분리 과정에서 그 죽일 놈의 도메인이 뭔지 따지던 과정에서 시간을 너무 많이 한 탓일까..팀플이 너무 많았던 탓일까....암튼, 다시 다 뒤엎고 처음부터 새 판을 짤 수 있을 시간은..솔직히 없었다..  할 수 없이 울며 겨자먹기로 public으로 돌리기를 선택했다. 단 멤버변수들은 private로 그대로 둬서 나름대로의 양심(?)은 챙겼다. 그럼에도 불구하고 메서드 단위로 테스트할 수 없는 부분이 존재했다. 예를 들어 LottoGameResult의 멤버변수로 earningRate(수익률) 변수를 뒀는데, calcaulateEarningRate를 수행한 다음 수익률을 확인할 수 있는 방법이 없었다. 왜냐면 내가 멤버변수들의 접근지정자마저 public으로 돌릴 순 없었서 private로 남겨뒀으니까!! 내 마지막 자존심이니까!! ㅠㅜ.. 이 부분은 할 수 없이 calculateEarningRate의 수행을 포함하는 더 큰 메서드를 수행하고 내가 원하는 로직이 이뤄지는지 확인하는 식으로 코드를 짰다.

 

 도메인 로직에 대한 단위 테스트 작성에 대한 연습이라..클래스 별로 단위 테스트를 진행하도록 만들면서 테스트 코드가 좀 더 깔끔해지는 느낌을 받았고, 도메인 로직들에 대해서만 테스트하며 중요한 로직에 대한 테스트 위주로 진행하는 게 중요하겠다라는 느낌도 받았지만..결국 아쉬운 감정이 크게 남는다. 좀 더 클래스 분리를 잘했더라면 어땠을까. 기능 구현 중간중간에 지금까지 만든 기능에 대한 테스트 코드를 작성하는 식으로 했으면 어땠을까. 등등과 같은..


그래도 성장했고, 배운 건 많다. 

 정말 힘든 고통의 1주일을 보낸 것 같다. 4개의 팀플, 가족모임, 알바를 제외한 모든 시간을 쏟아부었고 학교 개인과제는 전부 버리면서까지 시간을 투자했지만, 벽을 느끼는 순간들이 있었다고 할 수 있을 정도로 고통스러웠고 마무리한 후 뒤돌아보는 지금 이 시점에서도 개운함보다는 아쉬움과 찝찝함이 더 남는다. 지금 이 순간조차도 도메인에 대한 스스로의 기준이 구체적이지 않다. 이 죽일 놈의 도메인..

 

 그러나 뒤를 천천히 돌아보면, 물론 고통스러웠지만 그럼에도 배운 게 많다. 새로 배운 개념부터만 해도 enum이란 것이 있다. 열거형 이런 거 내가 3주차 미션을 시작하기 전까지는 잘 모르던 놈이었다. 그래도 이번 미션을 통해 enum이 뭔지를 알게 되고, 능숙하진 않아도 다루는 법을 알게 됐다. 클래스 분리를 통해 좀 더 읽기 좋고 재사용성이 좋은 코드를 짤 수 있다는 것도 알게 됐고, 구현이 다 끝나고 테스트 코드를 만드는 게 아니라 중간중간 만드는게 좀 더 좋다는 것도 느낄 수 있었다.

 

 고통스러운 시간이었고, 깨지고 부셔지는 시간들이었다. 그러나 전에 비해 성장한 게 느껴지고, 이 깨지고 부셔지는 것도 성장하는 과정에 포함되는 요소일거다. 성장하는 시간들이 달콤함만 있지는 않고 고통을 많이 수반할 거다. 그럼에도 포기하지 말고 나아가면, 더 큰 성장 더 맛있는 달콤함을 느낄 수 있을 것이다.

 

  이번 년도 롤드컵에서 DRX라는 팀이 우승했고, 데프트라는 선수가 데뷔 10년만에 우승하면서 "중요한 건 꺾이지 않는 마음"이라는 말을 했었다. 나도 꺾이지 않는 맘을 가지고 계속해서 나아가야겠다.

 

음..뭔가 오글거리는 것 같기도..

0. 다시 한 번 스스로에게 채찍질하기

2022년 11월 2일 15시. 14시에 있던 코수타의 종료와 함께 2주차 미션이 시작됐다. 미션이 시작되면서 프리코스를 밟는 동기들끼리 서로 피어리뷰나 토론 등을 할 수 있는 프리코스 커뮤니티도 활성화됐고, 공통 피드백들이 주어졌다. 

 

원래는 바로 2주차 미션을 수행하려 했으나, 14시에 들었던 코수타와 이후 다른 사람들과 진행한 상호 코드 리뷰(피어 리뷰)에서 얻은 게 많았다.

 

일단 제일 인상 깊었던 것은, 포비님이 했던 말이다. 기능 목록을 정의하는 것이나 메서드 분리 등은 정해진 답이 없기 때문에, 어떻게 해야 할까요? 라기 보다는 스스로 고민해서 자신의 기준을 만들어가야 한다는 것. 사실 나도 돌이켜보면 1주차 미션을 진행하는 중에 이게 좋을까 저게 좋을까를 많이 고민하는 과정에서 "보편적으로 답이 될 수 있는 무언가가 있다!"라는 걸 무의식적으로 가정하고 있던 것 같다. 그 과정에서 다른 사람들에게 물어가면서 어떻게 하는게 정답에 가까운지를 알아내야 한다고 생각했었다. 정해진 답을 찾아내야 하는 학생의 함정에 빠져 이건 답이 없는 현실의 문제임을 망각했던 거다..앞으로 개발자의 삶을 살아가는데 있어서 답이 없는 문제들을 많이 마주칠 것이다. 그리고 내가 프로그래머가 되고 싶은 이유는 내가 그렇게 마주치는 문제들, 즉 정해진 답이 없는 현실의 문제들을 코딩이란 도구를 통해 해결하는 사람이 되고 싶어서이다. 그러나 부끄럽게도 무의식적으로 정답을 찾으려고 했다니..일종의 고정관념이 머릿속에 있던 것 같아 망치로 탕 맞은 느낌이었다.

 

그리고 프론트를 공부하던 시절 강의에서 잠깐 봤던 임동준 코치님(준 님이라고 부르는 듯 하다)이 나와 피드백을 주는데, 요구사항을 의도적으로 명확하게 주지 않았음을 강조해주셨다. 이 이유 역시 포비님이 했던 말과 마찬가지로, 현실에서 우리가 개발자로써 풀어가야 할 문제들은 정답이 없을 뿐더러 명확한 요구사항이 주어지는 문제들이 아니기 때문! 즉 스스로 생각하고 스스로 고민하고 스스로 판단해서 기능 목록, 요구사항 목록들을 정의해가야 한다고 해주셨다. 이런 과정을 통해 학교 과제에서나 요구하는 정답 코드가 아닌, 현실 문제를 풀 수 있는 코드를 짤 수 있게 될 거라고 말씀해주셨다. 역시나 감명을 크게 느꼈다..

 

그리고 저녁에는, 부족한 실력임을 알고 있지만 용기를 내어 프리코스 커뮤니티를 통해 다른 사람들과 1주차 미션에 대한 피어리뷰를 진행했다. 다른 사람들이 짠 코드를 보면서 "이렇게 하면 확실히 다른 사람이 봤을 때 이 사람이 뭘 하고자 하는지 잘 알 수 있겠구나", "와 이걸 이렇게도 할 수 있구나", "이 부분은 이렇게 하면 좀 더 깔끔할 것 같다"  등등을 느끼면서 많은 배움을 얻을 수 있었다. 동시에 내 코드를 다른 사람이 리뷰해주는 과정을 통해 내가 짠 코드가 어떻게 읽히는지, 어떤 문제가 있는지, 어떤 부분을 개선해야 하는지도 여실히 느낄 수 있었다. 남이 나에게 해주는 코드리뷰와 내가 남에게 해주는 코드리뷰는 처음인데 이거 정말 맛있다..그리고 우테코는 사실상 읽기 좋은 코드를 쓰고 싶은, 어떻게 보면 나랑 같은 목표를 가진 사람들끼리 서로 모여서 피어리뷰를 해주는 거다보니까 신나기도 했다. 열정있는 사람들과 함께이니 더 끓어오르는 느낌. 

 

코수타와 피어리뷰를 통해, 정말 느끼고 얻은 게 많다. 정리하면 다음과 같다.

 

  1. 기능목록, 메서드 분리? 스스로 고민하고 스스로 쥐어짜서 해라. 정답 없다. 나만의 기준을 세워가면서 해라.
  2. 리터럴 값 (1, -1같은 거) 그대로 쓰지 말고 변수에 담아서 해보자. 그게 다른 사람 입장에서 이 수가 뭐하는 용도인지 잘 드러난다.
  3. 메서드 선언 순서도 아무 생각 없이 막 하지 마라. 코드의 흐름과 메서드 선언을 하는 순서를 맞춰두면 다른 사람 입장에서 그 메서드가 뭘 하는지 찾아가는 길이 편하다
  4. 한 메서드가 하나의 기능을 하게 해라. 한 메서드가 너무 많은 책임을 가지면 안 된다

 

그럼 내가 생각하는 가장 좋은 기능목록이란 뭘까. 1주차에서 느꼈던 것은, "의미있는 기능 단위로 작성된 목록"이 가장 좋은 기능목록이라는 것이고 내가 생각하는 의미있는 기능 단위란 각 기능들이 메서드 하나 또는 특정 코드에 대응하는 구조를 말한다. 물론 이건 저번 1주차 미션에서 "아 좀 깔끔하게 떨어지지 않는 구조로 세밀하게 기능목록 만들고 그거에 맞춰서 구현했더니 리팩토링할 때 메소드 단위로 뽑아내기 힘들더라.." 라는 걸 느꼈기 때문에, 내가 현재로써 생각하는 가장 좋은 기능목록에 불과할 수도 있다. 그러나 어떠랴. 이렇게 생각해보고 시도해보고, 그 다음엔 저렇게 해보고 시도해보는 과정에서 나만의 기준이 생길 것이고 이게 내 개발자 인생에 큰 도움이 될 거다. 계속해서 고민해보고 생각해봐야 할 사항일 것이다.

 

이번 2주차 미션에서는, 함수/메서드들을 역할에 따라 분리하는 것과 테스트 도구에 익숙해지는 것을 목표로 한다고 메일로 말씀해주셨다. 테스트 코드..사실 1주차 미션에서 휙하고 처음 마주쳤지만, 이 녀석과도 이제 안면을 트고 인사할 때가 된 거다. 계속해서 성장하고, 앞으로 나아가자. 바쁜 하루들이 계속되지만, 이 길이 내리막길이 아닌 오르막길임을 알고 있다. 


1. 구현 전 준비운동

우선 기능 목록을 정의하는 것부터 시작했다. 코치님들이 일부러 요구사항을 명확히 주지 않는다고 말해주신 만큼, 주어진 룰을 바탕으로 내가 어떤 제한과 기능을 추가해야 할 지 생각했다. 주로 제한사항들에 대한 것들을 생각해야 했는데, 내가 생각한 제한사항들은 다음과 같았다.

 

  1. 사용자 입력이 3글자면 안 된다
  2. 사용자 입력이 숫자가 아니면 안 된다
  3. 사용자 입력에 0이 있으면 안 된다(맞춰야 하는 3개의 수는 1 ~ 9 사이임)
  4. 사용자 입력에 중복된 수가 있으면 안 된다(사실상 반칙임)

 

이를 토대로 최대한 의미있는 기능목록(각 기능들이 하나의 메서드 혹은 코드에 대응하는)을 다음과 같이 만들었다.

처음부터 이 모습은 아니었고, 중간중간 수정을 거쳐 만들어진 최종 기능 목록이다

 

구현에 앞서 조금 생각을 했던 것은, 클래스를 분리해서 만들어보자는 것이었다. 이번 2주차 미션의 목표가 함수/메서드들을 역할에 따라 분리하는 걸 연습하는 것이기도 했고, 근본적으로 한 클래스가 하나의 책임만 갖게 하고 싶었다. 물론 기본적으로 실행되는 Application.java에 모든 코드를 작성해서 만들 수도 있다. 그러나 내 목표는 "읽기 좋고 재사용성이 좋은 코드를 짜는 개발자가 되는 것"이고 이를 위해선 객체지향적인 프로그래밍을 할 줄 알아야 하기 때문에, 최대한 클래스를 분리해서 각각의 코드를 짜는 걸 추가적으로 연습해보고 싶었다. 숫자야구게임기가 있다고 하면 

 

  1. 각종 메시지들이 출력되는 디스플레이장치
  2. 키보드처럼 입력을 받는 장치
  3. 게임 로직

 

이렇게 3개의 요소가 필요하다고 생각했다. 각각에 대응하는 GameMessageDisplay, Inputter, BaseBallGame이란 클래스를 만들고 Application에서 BaseBallGame객체를 만든 다음 runGame메서드를 통해 게임을 실행하는 방식으로 코드를 짜면 깔끔할 것 같다는 생각이 들었고, 그렇게 만들기로 했다.

 

그러나..

 

얼마되지 않아 사실상 입력장치의 필요성에 대한 의문이 들었다. 사실 입력받는 동작 자체는 우테코에서 제공하는 api를 사용하면 끝나는 부분이었고, 입력장치보다는 입력된 내용에 대한 검증(validate)를 필요하는 객체의 필요성이 더 높다고 느껴졌기 때문이다. 입력장치 객체 안에 검증메서드들을 넣는 식으로 만들까 했지만, 차라리 검증기 객체(Validator)를 따로 만드는 게 내가 처음에 원했던 "한 클래스가 하나의 책임만 갖게 하기"에 더 부합하다는 생각이 들었다. 그래서 입력을 받는 동작을 BaseBallGame으로 옮기기로 하고, Validator를 만들어 이 객체가 검증기능을 담당하도록 생각을 끝난 다음 구현에 들어갔다.


2. 학습 과정 -  한 메서드가 하나의 기능만 가지게 한다..그럼 그 기능의 범위는?

본격적인 구현에 들어가면서는 한 함수가 하나의 기능만 하도록 함수를 만드는 점에서 많은 고민을 하게 됐다. 함수 이름을 어떻게 지을 것인가! 부터 시작했는데, 이를 위해 메소드 이름 짓는 팁 등을 알아보다가 "동사로 시작하도록 이름 짓기"를 알게 돼서 일단 모두 동사로 시작하게끔 지어줬다. 원래는 gamePrepare라는 식으로 짓던 것도 prepareGame으로 다 바꿔줬는데, 확실히 동사로 시작하도록 통일하고 나니 이 메서드가 어떤 걸 하는지 그 의도가 조금은 더 잘 보이는 느낌이었다. 또한 검증메서드(isNumeric)등을 모두 긍정문으로 통일시켜줬는데, 이것 역시 그 의도가 좀 더 잘 보이는 것 같은 느낌이 들었다.

 

가장 많은 고민을 했던 건, "한 기능의 범위"를 어디까지로 봐야 하는가? 였다. 예를 들어서 내가 만들었던 getGameResult는 사용자가 입력한 수를 토대로 ball의 개수와 strike의 개수를 얻는 메서드였는데, 내부적으론 ball과 strike의 개수를 센 다음 그 개수들을 map에 저장하여 리턴하는 구조였다. 큰 틀에서 본다면 getGameResult는 "게임결과를 얻는다"라는 하나의 기능을 수행한다고 볼 수 있지만, 좀 더 파고들어 본다면 getGameResult는 "ball과 strke의 개수를 센다"와 "센 개수를 map에 저장한다"라는 두 기능을 수행한다고 볼 수 있다. 

바로 이게 고민이었던 거다. 사실 1주차 온보딩 미션에서도 이 부분을 많이 고민했었다. 도대체 어떤 관점에서 기능의 범위를 바라봐야 할까.. 목표 자체를 다시 상기해보면, "하나의 함수가 하나의 기능만 하게 한다"이다. 만약 세부적인 관점에서 본다면 getGameResult는 두 개의 기능을 하므로 잘못된 함수가 아닐까라는 생각이 들어서 많이 헷갈렸다.

결국은 큰 관점에서 "게임결과를 얻는다"라는 하나의 기능을 한다고 보고 일단 구현을 해나갔다.

 

그러나..

 

나중에 리팩터링을 하게 되면서 내 생각이 잘못됐음을 느낄 수 있었다.

우선, 돌이켜보니 함수의 덩치가 너무 크다. "결국은 큰 틀에선 ~~라는 기능을 하는 함수니까!"라고 생각하고 만들어서 그런지 내부적으론 여러 기능을 하는 구조였고, 결과적으로 함수 하나하나가 많이 뚱뚱했다.

또한, 함수 내에서 일어나는 세부적인 기능들이 어떤 걸 하는건지 눈에 쉽게 들어오지 않는다! 이게 가장 큰 문제였다.

 

 

이게 바로 당시의 getGameResult다. 큰 틀에선 "게임결과를 얻는다"라는 한 가지 기능을 하지만, 내부 코드들은 ball과 strike의 개수를 세는 기능과 센 개수를 map에 저장하는 기능을 한다. 그러나, 코드만 보면 뭘 하는 건지 한 눈에 파악하기 쉽지 않다.

 

따라서 읽기 좋은 코드를 만들기 위해, 이 부분들도 메소드로 추출하여 만들어야 할 필요성을 느꼈다. 그러면 다음과 같이 만들어질 수 있다.

 

 

getGameResult안의 코드들이 어떤 걸 하는지 한결 알아보기 편해졌다. 지금 글을 쓰며 다시 생각해보니 성공여부를 true로 바꿔주는 부분도 메서드화하고 이를 countBallAndStrike로 옮겼다면(예를 들면 judgeSuccess란 이름으로..) 더 좋았을 거라는 생각이 든다. 

 

회고를 쓰며 돌이켜보니, 역시나 아직도 부족한 면이 많다. 그러나 기능의 범위를 어디까지 해야 하는지에 대해 좀 더 감을 잡게 된 것 같다. "하나의 함수가 한 기능만 하게 한다"라는 것은, 기능별로 테스트를 할 때 그 기능에 맞는 함수를 사용할 수 있게 한다는 편리함을 위해서이기도 하고 다른 이유들도 있겠지만, 읽기 좋은 코드를 지향하기 위해서인 것 같다. 내가 헷갈렸던 것은 "내부적으로 파고들면 여러 기능이지만 큰 틀에선 하나의 기능으로 볼 수 있으니 더 이상 쪼갤 필요가 없지 않을까? "였다. 그러나 이번 과제를 통해 이에 대한 스스로의 답을 찾을 수 있었다. 그건 바로 "큰 틀에선 하나의 기능으로 볼 수 있지만, 읽기 좋은 코드를 위해 메서드 내부의 세부적인 각 기능들을 메서드화해야 한다!"이다. getGameResult의 예로 가보자. 물론 "게임결과를 얻는다"라는 궁극적인 기능을 하는 거지만  세부적으론 "ball & strike의 개수를 센다"와 "센 갯수를 저장한다"라는 2개의 기능을 통해 "게임결과를 얻는다"라는 기능을 하는 것이므로, 내부 기능들도 함수로 만들 필요가 있는 것이다! 하나의 기능(게임 결과를 얻는다)이 다른 기능들(개수를 센다, 센 갯수를 저장한다)을 통해 수행된다면, 그 다른 기능들에 해당하는 애들도 함수로 만들어줘야 한다는 것. (물론 필수는 아니겠지만 이렇게 하는게 읽기 좋은 코드가 된다) 이번 2주차 함수 분리하기 연습을 통해 얻은 나만의 작은 깨달음인 것 같다. 다만, 이 때 과하게 기능이 세분화되지 않도록 중간에 적절히 cut하는 것도 내 몫인 것 같다.


3. 학습 과정 - 테스트 코드 먹어본 후기

이번 2주차 미션의 또 다른 목표는 "테스트 도구에 익숙해지는 것!". 테스트 코드라는 걸 말로만 들었지 실제로 접한 건 사실 저번 1주차 미션이 처음이었다. 당시엔 "아니 뭐야 자바는 main이 있어야 실행할 수 있다는데 main이 없어도 된다고?"라는 것에 조금의 충격을 받았었는데, 이번 기회에 테스트 코드를 제대로 찍먹해보자는 생각이 들었다. 우선 구글링을 통해 테스트 코드가 어떤 걸 말하는 거고 왜 쓰는 건지, 내가 기존에 쓰던 나만의 테스트 방법인 "직접 프로그램을 수행하며 중간중간 print(js면 console.log)로 찍어보면서 확인하기"와 어떤 차별점이 있는지를 먼저 공부했다. 그러고나선 실제로 gradle로 자바 프로젝트를 만들어 assertj를 설치한 다음, 2가 1 + 1과 같은지 등을 확인하는 간단한 테스트 코드를 만들어가며 공부했다.

 

https://jofestudio.tistory.com/70

 

테스트 코드 찍먹해보기(feat. Junit, Assertj)

테스트 코드? : 내가 작성한 메서드가 실제로 잘 동작하는지 확인하는 용도로 작성하는 코드를 일컫는다. 이걸 왜 쓰는가? : 궁극적으론 기능이 정상적으로 동작하는지 확인하기 위함. 기존에 내

jofestudio.tistory.com

프론트에서 공부하다 백엔드로 전향해서 자바가 처음과 마찬가지였던 내 입장에선, 이 공부과정에서 gradle이 뭔지, 외부 라이브러리는 어떻게 설치하는지, annotation이 뭘 말하는 건지 하나도 몰랐다. 그래서 하나하나 구글링해보면서 gradle이 이런 거군..외부 라이브러리는 이렇게 설치하는군..등등을 공부했는데, 시간은 오래 걸렸지만 덕분에 자바를 조금이라도 더 알게 돼서 좋았다.

 

테스트 코드에 대해 공부를 하던 중 TDD라는 걸 알게 됐다. 테스트 코드를 먼저 짜고 그 테스트가 통과할 수 있는 프로덕션 코드를 짜는 방식이었는데, 이왕 테스트코드 찍먹할 거 TDD방식을 조금 적용해보면 어떨까란 생각이 들었다. 테스트를 먼저 짜면서 "어떻게 해아 프로덕션 코드를 테스트하기 쉽게 짜지?"를 고민하는 과정에서 "음 이런 기능을 하는 ~~라는 메소드를 만드는데, 리턴값을 어떤 걸 주게 하면 되겠다!"등을 생각할 수 있었고, 덕분에 하나의 함수가 한 기능만 하도록 하는 것에 나름대로의 도움을 얻을 수 있었다. 물론 처음이라 그런지 TDD방식 자체는 익숙하지 않고 상당히 불편하다는 느낌이다...

 

컴퓨터 숫자들을 세팅하고 그 숫자들이 서로 다른 수들이 세팅되는지, 3개가 세팅되는지 테스트하는 과정에서는 "이 테스트들은 컴퓨터 숫자들을 미리 세팅하고 테스트해야 하는데, 컴퓨터 숫자들을 세팅하는 걸 따로 뺄 순 없을까?"를 고민하다가 BeforeEach라는 어노테이션으로 각 테스트들의 수행 전에 실행될 함수들을 지정할 수 있는 걸 알게 됐다. 내 생각 이상으로 테스트 툴들은 여러 가지 기능을 제공해주던 거였다..또한 단순히 isEqualTo만 쓸 수 있는게 아니라 isFalse같은 여러 가지 메서드(assertion이라 부르는 듯하다)들이 있었고, 이것들을 체이닝 형식으로 이어서 여러 가지 조건을 만족하는지에 대한 테스트도 할 수 있는 걸 알게 되면서 엄청난 편리함을 느낄 수 있었다.

 

가장 놀랐던 건 따로 있다. 사실 야구 게임 프로그램 자체는 사용자 입력을 기반으로 하기 때문에, 사용자가 터미널에 입력하는 걸 받아서 테스트하는 방식은 힘들다고 생각했다. 실제 사용자 입력을 테스트하려면 프로그램 자체를 수행해야 한다고만 생각했었다. 따라서, 입력값에 대한 예외처리는 내가 직접 변수를 만들어 잘못된 입력을 할당한 다음, 다음과 같이 validate메서드 자체를 호출해서 잘못된 입력에 대한 처리를 테스트해야 한다고 생각했다.

 

 

근데 이건 내 착각이었다! ApplicationTest파일에 기본적으로 작성된 두 개의 test가 있었는데, 각각을 ctrl + b를 누르며 코드를 따라가며 분석하는 과정에서 프로그램을 실행시킨 다음 내가 직접 입력할 필요 없이 임의의 입력을 넣으며 테스트할 수 있는 걸 알게 됐기 때문이다!!

 

 

이게 우테코에서 기본적으로 ApplicationTest파일에 넣어줬던 테스트인데, 야구게임 프로그램을 실행하고 원래는 사용자가 터미널 창에 입력해야 하는 값을 "1234"라는 값으로 자기들이 대신 넣어줘서 테스트를 돌리는 코드다! 

 

 

이것 역시 우테코에서 기본적으러 넣어줬던 테스트로, 역시나 프로그램을 실행시킨 다음 사용자가 직접 입력할 필요 없이 "246", "135"등의 입력값을 자기들이 대신 넣어주는 테스트이다. 

이걸 알게 되고는 "와 그럼 내가 테스트할 수 있는 영역들이 그럼 엄청 넓어지는데? 사용자가 온갖 수들을 입력하는 과정 자체를 하나의 테스트로 만들 수도 있겠는데? ” 라는 생각이 들었고 테스트 코드의 미칠 듯한 강력함을 피부로 느낄 수 있었다. 기존에 내가 테스트했던 방식으로 한다면 게임 코드를 수정한 뒤 직접 야구 게임을 실행한 다음 컴퓨터를 숫자를 내가 하나하나 맞춰야 하는 번거로운 작업이 진행됐을 텐데, 수들을 입력하는 과정 자체를 하나의 테스트로 만들게 된다면 코드를 수정한 뒤엔 테스트 실행 버튼만 딸깍하고 누르면 된다. 테스트에 걸리는 시간 자체가 대폭 감소하게 되는 것..이게 사실 2주차 미션에서 얻은 가장 큰 수확이 아닌가 싶다. 테스트 코드라는 걸 찍어먹어보고 싶었던 건데, 잠깐 찍먹한 것으로도 그 풍미를 온몸으로 느낄 수 있었다. 정말 강력한 도구라는 생각이 들고, 앞으로 내가 어떤 개발로 가든 테스트는 꼭 데리고 가야겠다.

 

테스트의 장점을 느낀 건 이뿐만이 아니다. 코드 수정 후 테스트를 하는 것을 통해 문제가 나는 부분을 파악할 수 있었다. 그 상황은 다음과 같았다.

 

 

어느 테스트가 실패했는지, 어느 부분에 문제가 생겼었는지를 파악하기 쉬웠다. 즉 코드를 수정한 후 테스트를 돌려봄으로써 어떤 부분이 잘못 돌아가는지를 쉽게 파악할 수 있었다! 코드를 수정하는 과정에서 기존 기능들이 잘 동작하는지 파악하게 하는 데에도 테스트가 큰 도움을 주는 것을 느낄 수 있었다. 


4. 그 외의 고민 - 어떤 흐름으로 짜야 다른 사람이 읽기 편할까

학습 목표 이외에 2주차 미션을 수행하며 고민했던 것 중 하나만 꼽자면, "어떤 흐름으로 짜야 다른 사람이 읽기 편할까?"가 아니었나 싶다. 동일한 동작을 하게 된다고 해도, 그 흐름을 짜는 방식은 천차만별이다. 예를 들면 예외처리가 그런 종류일 것이다. 사용자 입력을 검증하면서 문제가 있으면 에러를 일으키는데, 어디서 일으킬지는 내가 정해야 한다! 처음엔 다음과 같이 validate메서드 자체에서 문제가 있으면 에러를 일으키도록 코드를 짰었다.

 

 

그러나 이 경우, 다음과 같은 문제가 있었다. 다음 코드에서 isValidInput은 내부적으로 위에  있는 validateInput메서드를 호출한다.

 

 

즉 이 코드는 사용자에게 입력을 받고, 유효성 검사를 통과해야 게임결과를 출력하는 코드다. 나야 이 게임을 만들었고, 다른 우테코 참가자들이야 사용자 입력이 유효하지 않다면 에러가 난다는 것은 암묵적으로 알고 있을 것이다. 그러나 아예 다른 사람이 보기엔 어떤가. "음 사용자한테 입력받네. 그 입력이 유효할 때만 게임결과 출력하고, 입력이 유효하지 않으면 아무 일도 없네"라고 오해할 수 있는 코드라고 생각됐다. 왜냐하면 그 사람들은 입력이 유효하지 않으면 에러를 내야 한다는 걸 모르니까. 즉 메서드 이름을 나름 잘 지었다고 생각했지만, 다른 사람이 보기에 맥락상 오해할 여지가 있는 코드인 것이다!

따라서 validate메서드를 유효하지 않으면 오류가 나는 코드에서 유효하지 않으면 VALIDATE_FAIL(=false)를 리턴하도록 바꿔주고, 다음과 같이 코드를 수정했다.

 

 

유효성 검사가 실패한 경우, raiseError 메서드를 통해 에러를 일으키는 것을 파악할 수 있게끔 바꿨다. 이제 다른 사람이 봤을 때도 "음 유효성 검사를 실패하면 오류가 나고, 성공했을 때만 게임결과를 출력하는군!"이란 생각을 할 수 있을 거라고 생각했다. 이를 통해 코드의 흐름을 어떻게 짜야 할지, 같은 동작이라도 어느 메서드에서 하게 할지에 따라 읽기 좋은 코드가 될 수도 있고 안 될 수도 있다는 걸 느꼈다.


5. 2주차가 끝나고..

저번 1주차가 끝나고 2주차로 접어들며 세웠던 목표였던 "의미있는 기능 목록 세우기"를 돌아봤다. 과연 목록의 각 기능이 하나의 메서드에 대응되도록 기능 목록을 만들었는가!

결과는 다음과 같다.

 

게임 종료와 재시작의 경우, while문의 로직으로 대체되어 별도로 메서드화하진 않았다. 나름 기능 목록을 의미있게, 즉 각 기능들이  더 이상 세분화하지 않아도 되면서 하나의 메서드에 대응하게끔 잘 만든 것 같아 뿌듯하다.

 

전반적으로 이번 2주차 미션을 통해서 내가 궁극적으로 지향하는 목표인 “읽기 좋고 재사용성이 좋은 코드를 짜는 개발자 되기”의 방법 중 하나가 “함수를 최대한으로 분리하기”임을 느낄 수 있었고 덕분에 이전보다 스스로가 조금은 성장한 것 같아 기분이 좋다. 또한 테스트 코드라는 신문물을 알게 됐고, 앞으로 개발 속도 자체를 대폭 줄이면서 유지보수에 좋은 코드를 짜는 것에도 도움이 될 수 있을 것 같아 더욱 더 좋다. 테스트 코드라는 정신건강에도 좋고 맛도 좋은 S급 별미를 왜 이제야 맛을 보게 됐는지 억울하기도 하다... 우테코를 통해 조금씩 성장하고 있는 내 모습을 조금씩 보게 되는 것 같아 너무나도 감사하다. 앞으로도 우테코를 통해 더욱 더 성장하고 싶다.

 

+ Recent posts