녕의 학습 기록
레벨 1 - 블랙잭 회고 본문
레벨 1의 세번째 미션인 블랙잭이 끝났다. 이전 미션이 출석이었어서 그런지 이번 미션은 비교적 여유롭다고 느껴졌다.
상속 vs 조합
이번 블랙잭의 핵심은 딜러와 플레이어에서 발생하는 중복 로직들을 해결하는 것이었다. 주로 나온 방법들이 상속과 조합(합성과 같음)이었고, 나는 상속이 더 익숙했기 때문에 상속을 택했다. 물론 "딜러 is A 겜블러"라는 상속 관계가 적절하다고 생각해서 상속을 선택했기도 하다. 딜러와 플레이어는 모두 게임에 참가하는 겜블러라고 보았고 겜블러를 추상 클래스로 만들어 딜러와 플레이어가 상속 받게 했다. 그리고 딜러와 플레이어의 로직 차이는 초반에 공개하는 카드의 장수 차이만 존재한다고 생각해서 겜블러의 getInitialCards를 추상 메서드로 선언하였다. 이렇게 중복되는 로직은 부모 클래스인 겜블러에 구현하고 차이가 생기는 부분만 자식 클래스에서 오버라이딩 하니 중복 문제를 해결할 수 있었다. 그리고 상속을 이용했기 때문에 딜러와 플레이어를 겜블러라는 하나의 상위 클래스 타입으로 다룰 수 있다는 다형성의 이점을 누릴 수 있었다. 덕분에 Round 객체에서는 딜러와 플레이어를 하나의 리스트로 보관하며 이름으로 구분하고 인스턴스 타입을 구분할 필요가 없었다.
리뷰어인 엘리는 1단계에서 구현한 상속도 너무 좋지만 2단계에서는 조합을 사용해보고 둘의 장단점을 직접 학습해볼 것을 추천했다. 잘 모르겠을 때는 일단 해보면 장단점이 보일 것이라는 사실을 이전 미션에서 확인했었기에 엘리의 조언에 힘입어 2단계에서는 조합으로 변경해보았다. 그 과정에서는 두번의 시행착오를 겪었다.
첫번째 시행 착오는 겜블러 추상 클래스를 없애지 않고 인터페이스로 변경하여 딜러와 플레이어가 이를 구현하게 한 것이었다. 그리고 조합을 사용하기 위해 딜러가 플레이어를 필드로 가지고 있게끔 구현했다. 즉 딜러가 플레이어를 필드로 가지는데 딜러와 플레이어는 모두 겜블러를 구현하고 있다. 이러한 설계가 나온 이유는 상속에서 조합으로 바꿔야하는 이유에서 나왔다. 상속 시 자식이 부모의 구현을 전부 알고 있어 부모에 강결합 되는 것이 문제이기 때문에 인터페이스를 사용하면 구현으로 인한 강결합이 약해지지 않을까 생각했다. 거기다가 딜러와 플레이어가 같은 겜블러 인터페이스를 구현하니 조합을 사용하고도 Round 객체 내부에서 다형성의 이점을 누릴 수 있을 것이라고 생각했다.
이에 대해 엘리는 인터페이스 또한 상속이라고 했다. 내가 아는 상속은 extends 뿐이었고 왜 implements를 상속이라고 하는 지 처음에는 이해가 되지 않았다. 하지만 다른 크루들과 함께 이에 대해 논의를 하던 중 다음과 같은 인사이트를 얻을 수 있었다.
"시간이 지나면서 추상 클래스와 인터페이스 간의 경계가 허물어지고 모호해졌다."
관련해서 알아보니 java8 이후로 인터페이스에 default 메서드, static 메서드 등이 등장하며 이러한 변화가 생긴 것 같았다. 이러한 관점을 받아들인다면 결국 현재 구조는 상속과 조합을 같이 사용하는 꼴이 되는 것이다. 그렇다고 크게 문제될 것은 없지만 현재 내 목표는 상속을 조합으로 리팩터링하는 것이기 때문에 인터페이스의 필요성에 대해 의문을 제기할 수 있었다.
그리고 Round에서 딜러와 플레이어 간의 다형성이 꼭 필요할까라는 의구심이 들기 시작했다. 사실 블랙잭에서 딜러와 플레이어는 역할의 차이를 명확하게 가지는데 여기서 다형성을 챙긴다고 과연 어떤 이점이 있을 지 고민해보았고 스스로에게 마땅한 답변을 내놓지 못했다. "딜러 is A 겜블러"도 다시 생각해보니 관계가 애매하다고 느꼈다. 결국 큰 이유도 없이 다형성에 매몰되어 더 좋은 구조를 보지 못하고 있다고 판단하여 다형성을 포기하기로 했다. 분명 이 결정에 대해 트레이드 오프가 존재하겠지만, 현재로써는 이러한 판단이 옳다고 생각해서 인터페이스를 삭제했다.
두번째 시행 착오는 딜러가 플레이러를 필드로 가지도록 조합한 것이다. 보통 "딜러 is A 겜블러 && 플레이어 is A 겜블러" 구조의 상속을 조합으로 변경한다고 하면 "딜러 has A 겜블러 && 플레이어 has A 겜블러"로 변경한다. 반면에 나는 "딜러 has A 플레이어" 구조로 변경을 했다. 코드만 봤을 때는 깔끔하고 좋아 보였지만 베팅 기능을 추가하려고 하니 단점이 보이기 시작했다. 딜러는 베팅을 할 수 없고 플레이어는 베팅을 할 수 있다. 따라서 플레이어에게만 베팅과 관련된 메서드를 추가할 예정이었지만 딜러가 플레이어를 가지고 있기 때문에 딜러도 베팅 기능을 이용할 수 있는 가능성이 있었다. 즉 플레이어와 딜러의 역할이 다른데 딜러가 플레이어를 가지고 있으면 플레이어의 역할이 외부 인터페이스(public 메서드)를 통해 딜러에게 모두 노출된다는 문제가 생겼다. 이는 확장성을 저해하는 명백한 단점이라고 생각해서 딜러와 플레이어의 공통 로직을 겜블러에 구현하고 "딜러 has A 겜블러 && 플레이어 has A 겜블러" 구조로 변경을 했다. 이렇게 하니 딜러와 플레이어는 각각의 역할에만 집중할 수 있다는 장점이 있었다.
이로써 1단계에서는 상속, 2단계에서는 조합을 모두 사용해보았다. 사실 내 기준에서는 두 방식 모두 좋았지만 한번 장단점을 생각해봤다. 우선 상속은 중복 코드를 해결할 수 있으며 다형성을 활용할 수 있다는 장점이 있다. 하지만 컴파일 시점에 자식 클래스가 부모 클래스에 강결합된다는 단점이 있다.(몸소 겪어보지는 못함) 이에 비해 조합은 런타임 중에도 구현체를 갈아끼워 변경할 수 있다는 장점이 있다. 또한 중복 코드도 해결할 수 있다. 하지만 다형성을 사용하지 못하기 때문에 서로 다른 타입으로 다루어야한다. 이로 인해 분기문이 다수 발생하기도 한다.
나름 유명한 매치업인만큼 명확한 트레이드 오프가 존재하는 거 같다. 그렇기 때문에 앞으로는 상황에 따라, 그때그때 장단점을 비교해보고 무엇을 사용할 지 근거를 따져가며 선택하면 될 거 같다.
슬럼프..?
블랙잭은 비교적 여유로운 미션이었지만 그 여유가 나를 힘들게했다. 우테코 시작하고 한달째인 지금, 주변의 크루들은 이펙티브 자바, 오브젝트 등 다양한 스터디를 하고 있다. 나는 아직 레벨1이기 때문에 적응 차원에서 미션과 주어지는 학습 거리들에 집중하기로 했다. 우테코에서도 다른 사람과 비교하지말고 나의 학습 속도에 집중하라고 하지 않던가.
그러다 며칠 전에 미션 pr을 제출하고 나고 나니 2-3시간이 비게 되었는데, 이 시간을 어떻게 써야할지 감이 잡히지 않았다. 다른 크루들은 이 시간에도 스터디를 하면서 끊임없이 학습하고 있을텐데, 내가 너무 안일하게 지내고있나하는 생각이 들며 괜스레 조급해지기 시작했다. 그럼에도 뭔가를 시작하지 못해 매우 스트레스 받았었다.
마음이 조급해지니 돌아온 pr 리뷰를 반영하는데도 집중하지 못하고 미션에 흥미를 잃었다. 결국 리뷰어인 엘리에게 직접 dm을 보내 도움을 요청했다.

흔쾌히 승낙해주셔서 설계에 대한 얘기를 주고 받았고 엘리는 내게 다음과 같은 조언을 해주셨다.

좋은 말을 해주셔서 그런지 비교적 금방 안정되었고 블랙잭 미션을 무사히 마무리 할 수 있었다..!
그래도 앞으로 같은 상황이 생기지 않았으면 한다. 마침 회고를 수정하는 시점에 6기 선배분들이 오셔서 다양한 정보들을 공유해주셨다. 그 중 6기 짱수가 나처럼 이맘때쯤 조급해했던 경험담을 들려주며 여러 조언을 해주었는데 너무 공감이 가서 눈물이 핑돌았다.(스트레스를 꽤 받았었나보다) 전부터 학연으로 연락을 주고 받았던 6기 리니와도 실제로 만나 여러 얘기를 주고 받을 수 있었다.
선수타 이후로는 다음과 같은 것들을 느꼈다.
- 현재 내게 필요한 것에 집중하자.
- 미션하며 애매했던 것들 따로 빼놓았다가 시간 남을때 파고들자.
- 정 할게 없다고 느끼면 쉬는 것도 좋다.
- 우테코에 1년 있으면서 크루, 리뷰어, 코치들을 잘 이용하면 좋다.
- 이래도 좋고 저래도 좋다. 자신의 선택에 책임을 질 수 있으면 된다.
- 무엇보다 앞으로 힘들 날이 훨씬 많다. 이딴게 슬럼프?
나와 똑같은 길을 걸었던 사람들의 조언은 정말 귀중한 거 같다. 6기 분들에게, 특히 리니에게 감사하다.
페어 프로그래밍
내가 라젤이 누군지 모를 적에 라젤 바로 앞에서 ‘라젤이 누군데’를 시전한적이 있었는데 공교롭게도 일주일도 안돼서 라젤과 페어를 하게 됐다. 이전 미션에서 젠슨과 속도를 맞추지 못해 아쉬웠던 점이 많았던 지라, 이번 미션만큼은 라젤의 속도에 맞추기로 해봤다. 그래서 첫날 이후로는 라젤에게 주로 드라이버 역할을 맡기고 나는 옆에서 지켜보며 페이스를 맞추려고 노력했다. 이전에 젠슨이랑 하며 혼자 앞서 나갔던 때와는 다르게 확실히 페어와 호흡을 잘 맞출 수 있었다. 그리고 너무 세세하게 페어의 구현에 관여를 안해도 되겠다는생각을 했다. 내가 세부적인 코드에 대해 무언가를 말할 때면 이미 라젤이 내가 생각했던 것과 유사한 방식으로 구현하고 있었기 때문이다. 그런 점에서 평소보다 페어에게 더 신뢰를 가진 채 시간을 준다면 더 원활한 페어 프로그래밍을 할 수 있을 거 같다.
추가로 라젤이 정말 대단한 사람이라고 느꼈다. 7기 크루라면 모를 수가 없는 라젤은 무조건 E일 거 같은데 I라고 한다. 그리고 매일 11시에 퇴근해 6시간도 되지 않는 수면 시간을 가지면서 모두에게 언제나 밝은 모습을 보여준다. 처음에는 원래 그런 사람인가 싶었지만 라젤도 이러한 변화를 목표로 우테코 내에서 실험 계획을 수행하고 있다는 얘기를 듣고서는 대단하다고 생각했다. 그런 라젤을 보면서 나도 많은 사람들과 더 웃으면서 지내려고 노력하게 되는 긍정적 영향을 받아간다.
피드백

라젤이 내가 생각한 나의 개선할 점을 정확하게 집어주어 솔직히 놀랐다. 요즘하는 개발 고민의 대다수가 결국 오버 엔지니어링이라는 생각과 함께 끝을 내린다. 하지만 그러한 오버 엔지니어링마저도 우테코에서만 할 수 있는 영역이지 않을까 생각한다. 모르겠으면 일단 해보고 나만의 정답을 찾아가면 된다. 정답이 안나온다면 다른 크루 리뷰어 코치들의 도움을 받으면 된다. 물론 시간은 제한되어 있기에 우선순위를 따져가며 특수한 경우에 적용해볼 수 있을 것이다.
전체적으로는 라젤도 긍정적인 피드백을 써주었다. 물론 안좋은 면이 있더라도 대놓고 적기는 힘들다고 생각한다. 언젠가부터 나는 페어에게 나의 단점이 있다면 정말 솔직하게 써달라고 말을 하기 때문에. 이후 페어가 작성해주신 피드백에 대해서는 그냥 그게 페어가 본 내 모습이라고 생각하고 믿기로 했다. 그런 점에서 라젤이 즐겁고 유익했다고 느꼈다니 정말 다행이다..!

성장하기 위해 집중해야할 한가지로 assertions를 활용한 더 풍부한 테스트 코드를 학습해 볼 것을 집어주셨다. 평소 테스트하는 것을 좋아하기 때문에 말씀해주신 것처럼 테스트 코드에 대해 더 공부해보면 좋을 거 같다!
마치며
어느덧 레벨 1의 마지막 미션만을 앞두고 있다. 시간은 왜 이리 빠르고 왜 이리 정신이 없는지.. 눈떠보면 금요일이다. 카톡이 오면 읽어놓고 기억하지 못한다.
no pain no gain..
아무래도 회고인데 일기의 느낌이 강해서 좋았던 것/아쉬웠던 것/해보면 좋을 것을 짚어보며 블랙잭 미션 회고를 마무리 하고자 한다.
좋았던 것
- 하나의 문제에 대해 상속과 조합을 모두 사용해보며 장단점을 학습했다.
- 페어와 속도를 맞추며 미션을 해결했다.
아쉬웠던 것
- 외부에 휘둘려 조급한 마음을 가졌다.
- 얕게 훑어가는 방식으로 학습해 시간이 남는 거 같다.
해보면 좋을 것
- 조급해하지 않기.
- 문제에 대해 적절히 깊게 학습하기.
- 가끔은 일단 하고 보기.
'대외활동 > 우아한테크코스 7기' 카테고리의 다른 글
레벨 1 - 출석 회고 (4) | 2025.03.03 |
---|---|
레벨 1 - 로또 회고 (1) | 2025.02.20 |
우아한테크코스 7기 웹 백엔드 최종 합격 후기 (4) | 2024.12.30 |
우테코 7기 1차 합격 및 최종 코딩테스트 후기 (4) | 2024.12.17 |
[프리코스 2주차] gitignore 분석하기 (0) | 2024.10.24 |