[우아한 테크코스] Level1 회고
4월 4일 금요일로 우아한 테크코스 7기 Level1 과정이 끝났다. 프로그래밍을 제대로 배운 것은 처음이었고, 할 일도 엄청나게 많았지만 다시 돌아보니 매우 재미있었다. 크루들과 만나고, 프로그래밍을 하는 과정이 너무 즐거워서 잠실 캠퍼스에 등교하는 것이 기다려졌을 정도다. 이번 글에서 레벨1이 어떻게 지나갔는지 짧게 회고해본다.
1. 소프트스킬
부트캠프의 회고인데 소프트스킬이 먼저 나오는게 이상하지 않을 정도로, 우테코에서는 소프트스킬을 강조한다. Level1이 끝난 지금 회고해본다면, 단순히 프로그래밍을 잘 하는 사람
을 길러내는 것이 아니라 문제 해결을 위한 더 나은 사람
을 길러내는 곳이었다.
1.1 연극
우테코에 들어와서 가장 먼저 한 활동도 프로그래밍에 관한 무언가가 아니라 연극이었다. 생면부지의 4명을 모아놓고 10분 분량의 연극을 해야 했다. 환경도 익숙하지 않은데 이런 미션을 받으니 마치 이세계에 떨어진 기분이었다.
다행히 좋은 팀원들과 만나서 (Shout out to 22 Group) 큰 어려움 없이 연극을 마쳤다. 뒤이어 이어질 Level1의 메인 이벤트인 페어 프로그래밍 이전에, 사람들과 소통하는 과정을 미리 “연극”
이라는 매질로 전달한 느낌이다.
1.2 데일리 미팅
우리 연극조와 다른 조를 합쳐 대략 10명 정도의 인원이 등교 직후 30분정도 짧은 대화를 해야했다. 지금에 와서야 이 데일리 미팅조가 너무 좋지만, 처음엔 이 시간이 꽤나 긴장됐다.
대화 주제는 매우 다양했다. 정석적으로 자신의 헬스체크를 하거나 현재 고민하고 있는 것을 말할 때도 있었고, 친목 도모를 위한 게임을 하는 경우도 많았다. 내가 Level1를 즐겁게 다닌 이유의 7할은 이 데일리조 때문이라고 해도 과언이 아니다.
1.3 유연함 강화 스터디
이미 유강스에 대해서 회고록을 올렸으므로 길게 적지는 않겠지만, 정말 알찬 활동이었다.
유연해지기 위한 목표를 2~3개 정도 잡고, 매주 해당 목표를 달성하기 위한 실험 계획을 짠 후 실천한다. 이후 내 활동에 대해 짧게 글을 남기고, 팀원들과 피드백을 주고받는 활동이다.
2. 페어 프로그래밍
2.1 로또
기간이 너무 짧아서 무언가 얻어가기엔 부족했다. Junit과 AssertJ를 둘 다 사용해 본 경험이라던가, 인텔리제이 단축키 정도? 짧게 정리하자면…
- AssertJ는 메서드 체이닝이 가능하다. 그로 인해 더 깔끔한 코드 작성이 된다.
- Junit5보다 직관적이다. Junit5는 사용시 어디가 정답이고 예측값인지 넣을때마다 헷갈리는데 AssertJ는 그렇지 않다.
AssertJ나 열심히 써야겠다. 굳이 안 쓸 이유가 없는듯
2.2 출석
많은 크루들을 울렸던 최종 테스트 출석 프로그램을 페어와 다시 구현했다. 이번 미션에서 강조한 것은 올바른 TDD 방법이었다.
올바른 TDD의 순서
- 실패하는 테스트를 만든다.
- 가장 빠르게 테스트가 성공하도록 구현한다.
- 리팩토링한다.
미션2에서 엄격하게 적용하려고 하니 너무 어려웠다. 처음에는 잘 되나 했지만, 뒤로 갈수록 기능 먼저 만들고 테스트를 짜고 있었다.
이후 미션에서도 TDD를 하면서 느낀 나의 감상은 아래와 같다.
- TDD는 설계 방법 중 하나이다. 테스트는 부가 기능으로 따라오는것.
- 테스트를 치밀하게 짜야 한다. 설계가 먼저다.
- 모든 설계를 Bottom - up 으로 짜려면 전체 그림이 보이지 않아서 어렵다. Top - down을 적절하게 섞어서 활용하자.
- 해당 클래스나 메서드에게 바라는 최종 목표를 생각하자.
특히 마지막 부분은 마지막 장기 미션에서 통감했다. 장기 말의 이동에서 최종 목표가 목적지 라는 것을 미리 파악했다면 테스트를 미리 짜기 쉬웠을 것이다. 나는 장기의 이동 루트 전체를 갖고 있어서 최종적으로 테스트 하는 것이 매우 어려웠다.
나중에 체스 말의 이동을 구현하면서 최종 목표를 미리 잡고 구현하니, 매우 깔끔하게 구현됐던 기억이 있다.
객체 분리
Dto에 객체를 파라미터로 넘겨준 후, 메서드로 값을 지정했더니, 리뷰어에게 이런 답변을 받았다.
저는 DTO가 데이터의 전달 역할 정도만 해야 한다 생각합니다.
도메인 객체를 통해 calculate 메서드를 통해 계산을 해 결과 상태를가지고 있다는 것은 도메인 객체로서의 역할을 하고 있는 것 아닐까요?
예를 들어, 자동차라는 객체가 연료의 양을 계산해 가지고 있는다면, 이는 DTO가 아니라 도메인의 상태를 갖고 있는 것 아닐까요
DTO 에서 객체에 메세지를 보내 상태를 갖고 있는 것은 목적에 어긋나 보인다고 생각해요!
음… 확실히 맞는 말이다. 계산을 할 정도로 중요한 값이라면, 그냥 그 값을 위한 객체를 만들거나, 계산하는 클래스 내에서 상태로 갖고 있는게 더 직관적일 것이다.
내가 한건 그냥 domain을 view에게 넘겨준 꼴이나 다름 없는 것 같다. 이후 미션에서는 Dto 를 사용하지 않아서 그냥 view에 domain을 넘겨줬지만, 명심하고 다음 레벨 미션에서 사용해야겠다.
커맨드 패턴
그 이외에도 리뷰어에게 조언받은 커맨드 패턴
을 처음 사용해봤다. 입력받을 때 if문이 너무 많다면 고려해볼 선택지 같다.
2.3 블랙잭
처음 만난 낯선 도메인의 문제였다. 이번 미션에서는 상속과 조합
이 메인 키워드였다.
상속과 조합
많은 크루들이 Dealer
와 Player
를 Gamer
(혹은 Participant
)를 상속하는 클래스로 구현했다. 나도 그랬다.
하지만 점점 구현하다 보니 추상클래스가 그냥 Cards
만을 필드로 갖고 있는 일종의 일급 컬렉션처럼 되어버렸다. 그렇다면 굳이 추상클래스 상속이 아닌 조합으로 해결 가능할 것 같았다. 또한 Dealer
나 Player
가 다형성으로 묶일 일이(List<Gamer>
처럼) 별로 없었다. 하지만 상속은 유지했는데, 이유는…
- 결국 같은 필드가 있긴 하다.
- 결국 같은 메서드를 사용해야했다.
- is-a 관계가 확실한 것 같다.
만약 2번이 없었다면 그냥 조합을 사용했을 것 같다. 상속은 예상치 못한 사이드이펙트(특히 리펙토링 부분에서)가 많이 생길 것 같다.
이후 다른 코치에게 들었는데, 본인 취향이지만 추상클래스보단 interface를 사용하는 것이 더 좋다고 한다. 추상클래스는 처음에 설계하기가 너무 어렵다고 하던데… 나중에 한번 느껴봐야겠다.
Map 자료구조는 과연 객체지향적인가.
발단은 플레이어의 이름으로 플레이어에 대한 여러 정보들을 검색해야 했던 부분이다. 당연히 검색 기능이니 Map<PlayerName, Player>
형식으로 관리하고 있었는데, 막상 이렇게 만들고 보니 Player
의 필드로 PlayerName
이 전혀 필요없게 됐다. 막상 빼려고 보니 다음과 같은 고민에 빠지게 됐다.
- 플레이어 안에 이름이 없는건 좀 이상한데?
- 플레이어 이름을 플레이어 밖에서 관리하는게 맞나?
- 빼지 않고 유지한다면, 플레이어 이름은 두 곳에서 관리되어야 하는데 유지보수가 어렵지 않을까?
- 그럼 Map 자료구조는 객체지향적이지 않은가?
일단 Map으로 사용되던 부분을 모두 List로 교체한 후, 이 주제에 대해서 몇몇 크루들과 이야기해봤다. 듣고보니 그렇다 라는 크루도 있고, 그냥 검색 기능인데 DB의 인덱스처럼 사용하는거라 괜찮다라는 크루도 있었다.
만약 다시 만들게 된다면, Map 사용 후, 플레이어 안의 필드로 이름을 제거할 것 같다.(물론 규모가 엄청나게 커질 때의 이야기다.)
2.4 장기
레벨1의 마지막 미션인 만큼 어려운 미션이었다. 갑자기 도메인이 체스에서 장기로 변경됐는데, 그래서 더 곤혹스러웠다.
장기 미션에서 배운 점은 아래와 같다.
상속
,인터페이스
전략패턴
- 장기말을 어떤 요소로 인식할 수 있을까?
- 중복 코드의 정의
- DB
상속, 인터페이스 / 전락패턴 / 장기말 인식 / 중복 코드
한번에 적긴 했는데 다 이어지는 흐름이다. 처음에 너무나 당연하게 각각의 장기말이 장기 기물(Piece
) 추상클래스를 상속하도록 만들었다. 이후 각각의 이동을 추상클래스를 상속한 구체클래스 안에서 구현했다.
그렇게 구현하고 보니, 중복 코드가 꽤 많았다. 예를 들어서 장과 사는 이동이 아예 같고, 차와 포는 직선으로만 이동 가능한 부분이 유사했다. 그래서 이 부분을 하나로 뺄 수 없을까 고민하다가 인터페이스를 활용한 전략 패턴
을 생각했다.
문제는 전략 패턴으로 각각의 움직임 전략을 추상화하게 되니, 구체 클래스 내에 아무것도 남지 않게 됐다. ‘엥? 그러면 추상클래스가 필요 없는거 아니야? 장기 도메인에서 장기말은 움직임 그 자체가 아닌가? 그냥 piece
를 일반 클래스로 만들고 필드로 전략을 들고 있으면 되잖아?‘ 라는 생각이 들었다.
하지만 결국 이 방향대로 가진 않았다. 추상클래스를 유지하게 됐는데… 이유는
- 말의 이동과 상관 없이 중복되는 로직이 존재함.
- 사실 완전 같아보이는 코드지만 중복이 아니다.
내 장기말 이동 로직을 간단하게 설명하자면
- 장기말에서 다른 말과 상관없이 이동할 수 있는 경로를 계산한다.
- 유효하지 않은 경로(보드 바깥)를 제거한다.
- 장기말들 에서 다른 말의 위치를 더해 이동할 수 있는 경로를 선별한다.
예를 들어서, 내 코드에선 1의 과정에서 차와 포가 완전히 같은 로직이었다. 하지만 두 코드가 완전히 같다고 해서, 책임까지 완전히 같을 수는 없다. 만약 두 코드를 하나의 로직으로 만든 후 각각 갖고 있다고 해보자. 이 경우 포의 로직이 변경된다고 했을 때, 차의 로직까지 변경을 해줘야 한다. 이것은 SRP
를 위반한다. 즉, 이것은 중복 코드가 아니라는 결론에 이르렀다.
대신 3의 과정을 추상화하게 됐다. 포와, 포가 아닌 말, 궁성 안에서만 움직이는 말의 경로 선별 전략을 추상화 했다. 이렇게 해 보니 많은 if문을 줄일 수 있었다.
DB
궁성의 구현이 끝난 후 DB를 붙여야 했다. 아직 DB에 대한 이해가 부족해서 솔직히 말하자면 재미 없는 과정이었다. 물론, DB를 어떻게 현재 존재하는 도메인에 잘 붙일지 고민하는 과정은 꽤 좋았다.
붙이는 것까지는 어떻게 끝냈지만, 이걸 어떻게 테스트해야 할지는 고민이 됐다. 가장 먼저 생각한 것은 테스트 DB를 따로 띄우는 것이었다.
다른 크루들의 방식을 물어보니 인메모리 DB(H2)를 쓴 크루도 있었다. 모르는 방식이라 나중에 검색해 봐야겠다. 다른 방법도 알아보니 DB의 역할을 하는 Fake객체를 만드는 방식도 있었다. 테스트DB나 인메모리DB보다 훨씬 간편하지만, 이 방법이 얼마나 실제 DB 연결을 대표할 수 있는지는 의문이다.
3. 스터디
레벨1에서는 2개의 스터디에 참여했다.
- 모던 자바 인 액션
- 좋은 테스트
사실 좋은 테스트는 스터디라기 보다는 그냥 토론회 느낌이어서 딱히 준비할 것은 없었다.
3.1 모던 자바 인 액션 스터디
데일리 팀원 중, 모던자바에 관심 있는 크루들끼리 스터디를 하게 됐다. 개발 관련 서적을 한 번도 읽어본 적이 없는 터라, 이번 기회에 한번 읽어보기 위해서 스터디에 참여했다.
나는 2장 동작 파라미터화 와 3장 람다 표현식 을 발표했다. 2장 연속으로 발표하게 돼서 꽤나 쉽지 않은 스케쥴이었지만, 어찌저찌 잘 끝냈다.
각 챕터에서 심도 깊게 다뤄볼 부분들을 하나씩 했는데 다음과 같다.
각 이유는 내 블로그에 포스팅 되어있으니 링크를 걸어두겠다.
직접 스터디를 해보니 하나의 발표에 대해 여럿이서 답을 찾아가는 과정이 매우 좋았다. 하지면 여전히 내 발표부분이 아닌 파트는 책을 읽어갔음에도 이해도가 약간 떨어졌다. 그래도 혼자 했으면 그냥 넘어갔을 부분이 있을텐데, 스터디로 조금이나마 자세히 알고 가서 다행이었다.
3.2 좋은 테스트
우테코에서는 TDD를 많이 강조한다. 사실 TDD는 설계 방법 중 하나이고, 테스트는 부가적으로 따라오는 것이지만, 좋은 테스트를 짜는 것도 TDD에 도움이 될 것 같아 듣게 됐다.
나도 의제를 하나 냈는데, “리팩토링 시, 테스트 변경이 적은 코드 작성법” 이라는 주제였다. 토론의 결과는 아래와 같다.
- 테스트가 너무 많이 변경되는 것은 애초에 코드의 문제라기 보다는 설계가 문제였다.
- 세부 구현이 너무 강결합 되어있을수도 있다.
- 의존성 주입을 잘 활용하자.
- 메서드가 한 가지 일만 하게 하라.
어떤 크루는 TDD에 반하지만, 먼저 모두 완성 후 테스트를 만든하고 한다. 그러면 테스트를 수정할 필요가 없다고… 재밌는 답변이었다.
이외에도 기억나는 것은 TDD를 해야 하는 이유에 대한 것이었다.
- 메서드를 미리 작게 작성하는 연습이 된다.
- 먼저 테스트를 작성하면 테스트가 어려운 코드를 지양할 수 있다.
- 나중에 TDD를 하기 어려운 상황이 와도, 비슷한 퀄리티의 코드 작성이 된다.
사실 이전까지 TDD를 그냥 귀찮은 과정이라고 생각했는데, 듣고 보니 그럴 듯 했다.
4. 레벨 인터뷰
레벨1을 장식하는 마지막 활동이었다. 레벨1동안 자신이 무엇을 배웠는지 A4 한 페이지 내로 정리하고 크루들과 코치끼리 가상 면접을 했다. 나는 스터디에서 공부한 것, 페어 프로그래밍을 하면서 공부한 것 등을 적어서 냈다. 또 내가 인터뷰어의 역할도 해야했기에, 다른 크루들이 적어서 낸 내용도 조금 공부했다.
주로 코치가 주도해서 질문을 했는데, 내가 공들여 준비한 부분보다 다른 부분에서 많은 질문을 하셔서 조금 당황했다. 자료구조에 대해서 자세하게 물어보셨는데(Java에서 Stack 과 Queue, Deque의 차이점) 그래도 알고리즘 공부할때 어느정도 공부한 부분이라 꽤나 답변을 잘 했다고 생각한다.
나중에 레벨 인터뷰 팀원들이 써준 피드백을 보니 답변이나 인터뷰 스킬은 좋았지만, 자신이 사용해보지 못한 부분은 추상적인 설명이 주를 이뤄서 이해하기 어렵다고 했다. 내가 어느 부분이 부족한지 제대로 알 수 있어서 매우 좋았다. 내가 옵져버로 참관한 다른 크루의 인터뷰에선, 당황스러운 질문이 나와도 모든 인터뷰어와 눈을 맞추며 소통하는 크루가 있었다. 이런 부분은 참 배울만 하다고 느꼈다. 나도 그렇게 해봐야겠다.
5. 소감
어느새 약 2달간의 레벨1이 끝났다. 소프트스킬적으로든, 프로그래밍적으로든 스스로 배우고 느낀 점이 정말 많았다. 의식적으로 레벨1 동안 제대로 하지 못한 부분에 대해서 생각해봤다.
- 연속적인 집중 가져가기(1시간 이상 코드 잡고있지 말고, 반드시 중간에 휴식하기).
- 공식 문서를 보고 학습하기.
- 직접 연습해서 체화하기. 직접 써보지 않으면 이해가 잘 안된다.
해당 부분들에 대해서 의식적으로 해볼 계획이다. 레벨2는 프로그래밍 말고 여러 활동들이 계획되어 있어서 정말 바쁠 것으로 예상된다. 더 나아진 나를 상상하면서 지금의 휴식을 즐겨야겠다.
6. 사진
아마 첫 날 찍은 사진
창문 밖으로 보이는 잠실 풍경
크루들이랑 체스도 하고…
석촌 호수도 갔다옴…ㅋㅋ 오리가 귀엽다.
꽃 구경도 하고
잠실은 아니고 선릉캠