티스토리 뷰

블랙잭

블랙잭은 카지노 게임 중 하나로 트럼프 카드를 뽑아 21에 가까이 만들면 이기는 게임이다. 플레이어 간 대결을 하는 것이 아닌 각 플레이어는 딜러와 대결을 하게 된다. 딜러와 플레이어는 처음에 각 2장의 카드를 받고 게임을 시작한다. 플레이어의 카드는 모두 공개되며, 딜러의 카드는 한 장만 공개된다. 플레이어는 카드를 확인 후 더 뽑을지 결정할 수 있고 가지고 있는 카드 숫자의 합이 21을 넘는 경우 버스트로 딜러가 이기게 된다.

 

트럼프 카드에는 A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K의 수가 존재하는데 2~10은 나타내는 수가 점수가 되고 J, Q, K는 10점을 의미한다. A는 상황에 따라 1 또는 11 중 선택할 수 있다. 처음 받은 카드 2장 점수의 합이 21인 경우 블랙잭이라고 하는데 베팅 금액의 1.5배를 얻게 된다. 결국 21점을 넘지 않으며 21에 가깝게 만들면 되는 게임으로 이해할 수 있다. 그 외에도 다양한 규칙들이 존재하지만, 이번 미션에서는 설명한 정도의 요구사항만 구현해 보기로 했다.

게임 진행 순서

전체적인 진행 순서를 알면 도메인 설계에 도움이 될 수 있을 것이라 페어와 판단하고 진행순서를 정리해 보았다.

  1. 플레이어 이름 입력
  2. 각 플레이어들의 베팅 금액 입력
  3. 딜러와 플레이어에게 2장씩 나누어주기
  4. 나누어 준 카드 정보 출력
  5. 각 플레이어들의 선택 여부 입력
  6. 딜러 카드 결정
  7. 전체 결과 출력
  8. 수익 결과 출력

진행 순서를 바탕으로 어떠한 로직이 필요할지 페어와 얘기 후 어떤 객체에 어떤 책임과 역할을 부여할지와 어떤 메시지를 던질 수 있을지 고민해 보았다. 그 결과 아래와 같은 도메인 관계도를 구상할 수 있었다.

도메인 관계도

입력 뷰, 출력 뷰와 각 도메인 사이에서 중간 매개체로 컨트롤러가 동작하고, 도메인은 메시지를 던져가며 자신이 맡은 책임과 역할을 다하도록 한다. 블랙잭 게임이 참여자들, 덱, 베팅 테이블을 관리하고 있기에 컨트롤러는 캡슐화된 블랙잭 게임으로 메시지를 던지게 되고 내부적으로는 필드들에게 메시지를 던져서 전체적인 로직이 수행된다.

고민했던 부분

뷰에게 카드 목록, 카드 목록 점수의 합 정보를 함께 전달해야 하는가?

카드 목록과 카드 점수의 합을 출력해야 하기에 이에 대한 정보를 뷰에 전달해야 한다. 이 과정에서 고민이 발생하였다. 카드 목록에 대한 정보를 전달하면 뷰에서 계산이 가능한데, 같이 전달이 필요할까?

 

이 고민도 확실한 정답은 정해져 있지 않다. 하지만 기본적으로 모든 정보를 전달하는 것이 좋다. 함께 전달하면 뷰에서 점수를 계산하지 않아도 되기에 처리해야 할 데이터가 적어져서 뷰(클라이언트) 프로그램이 가볍고 효율적일 수 있다. 그러나 보안과 같은 이유로 점수 정보를 전달하지 않고 뷰에서 계산하는 경우도 있다. 이 경우에는 뷰에서 데이터를 처리해야 하므로 뷰(클라이언트) 프로그램이 조금 더 복잡해질 수 있다.

 

또한 뷰(클라이언트)에서 점수 합을 계산하는 방식이 잘못될 수 있기에 정확한 점수 합 정보를 전달하는 것이 안전하다. 뷰에서는 그저 출력만 담당하므로 코드 간결성도 높아지고, 유지보수성도 좋아진다.

게임의 승패여부는 어떤 도메인에게 물어보는 게 좋을까?

실제 현실세계에서 승패 여부를 판단하는 것은 딜러의 역할이다. 딜러는 자신의 카드와 플레이어의 카드를 비교해서 승패를 결정한다. 따라서 미션을 진행하면서 딜러가 승패여부를 결정하도록 설계하고 진행했다. 하지만 이 부분에 대해 명확하지 않았기에 리뷰어였던 토니에게 질문하였고 다음과 같은 답변을 해주셨다.

 

🌸 토니의 답변

꼭 현실세계의 도메인을 따를 필요는 없다고 생각해요. 현실의 블랙잭에서는 딜러만이 점수 계산이라는 행위를 하는 건 아니니까요. 저라면 점수는 결과 도메인이 받아서 비교하여 결과를 내주는 로직을 구현할 것 같습니다. 정답은 없기 때문에 구현하고 싶은 대로 리팩터링 해봐도 좋을 것 같아요. 전해드리고 싶은 말은 현실을 100% 따르지 않더라도 적절한 복잡도로 프로젝트 요구사항을 해결할 수 있도록 구현하는 것입니다.

 

실제 개발을 진행하면서 느꼈던 어려움에 답을 찾아갈 수 있게 해 준 리뷰였다. 현실세계의 도메인은 변화도 빠르고 매우 복잡하기에 100% 만족하는 애플리케이션을 만들기 위해서는 복잡성이 매우 높아질 것이고, 실제 구현하기도 어려울 수 있다. 이러한 복잡성을 잘 풀어 애플리케이션을 만들어내는 것도 개발자가 가져야 하는 역할이라고 생각하게 되었다.

학습한 부분

instanceOf 메서드 사용을 지양하라

자식 객체가 여러 타입일 때 특정 클래스가 맞는지 확인하기 위해 instanceOf라는 메서드를 사용한다. 블랙잭 미션에서도 딜러인지 플레이어인지 판별하기 위해 해당 메서드를 사용했다. 하지만 간편하게 클래스 타입을 확인할 수 있는 instanceOf를 사용하지 말고 다형성을 이용할 것이 권장된다. 그 이유는 무엇일까?

 

1. 캡슐화가 깨지게 된다.

instanceOf를 사용하는 경우, 객체가 무엇인지 외부 객체가 불필요하게 그 정보를 알게 된다. 때문에 해당 객체가 어떤 상태를 가지고 어떤 행위를 하는지 알아야 하는데, 이는 캡슐화가 깨진다는 것을 의미한다. 객체지향 프로그래밍에서 캡슐화가 깨진다면 본질을 잃어버리는 것과 같기에 instanceOf를 지양해야 하는 큰 이유 중 하나이다.

 

2. 새로운 객체가 추가되면 해당 메서드를 사용하고 있는 모든 함수를 찾아가서 고쳐야 한다.

새로운 객체가 추가된다면 instanceOf를 사용하고 있는 모든 곳에서 변화가 발생함을 의미한다. 확장에 유연하게 대처하지 못하기에 객체지향 원칙 중 하나인 OCP를 위반하게 되는 것이다.

 

3. 성능상 단점이 존재한다.

다형성을 적용하면 컴파일러는 어떤 타입의 메서드를 실행할지 알 수 없으므로 바이트 코드를 이용해 메서드에 대한 가상의 호출을 한다. 런타임에 특정 타입을 찾아 그에 맞는 구현을 실행하는 것이다. 하지만 instanceOf의 경우 알맞은 타입을 찾을 때까지 컴파일 시 모든 타입을 도며 검사해야 한다. 그로 인해 확인해야 하는 객체가 많아질수록, 성능 차이가 점점 커지게 될 것이다.

 

위와 같은 단점들이 존재할뿐더러 다형성을 이용했을 때 장점도 존재하기에 다형성을 적극적으로 활용하는 것이 좋다.

범용적인 DTO를 사용할 때

블랙잭 게임에서 딜러와 플레이어 모두 같은 정보를 전달하기에 범용적인 DTO를 만들어서 사용했다. 이에 리뷰어분께서 좋은 리뷰를 해주었기에 이에 대한 고민을 해볼 수 있었다.

 

🌸 토니의 의견

DTO는 계층 간의 정보를 이동하기 위한 객체죠. 지금은 도메인의 정보를 받아서 뷰로 이동시켜주고 있고요. 이 말은 DTO는 사용되는 곳의 요구사항에 따라 생성되고 변경된다는 점을 의미해요. 예를 들면 지금은 limit, isDrawn과 같은 값을 뷰에 반환하고 있죠. 하지만 요구사항이 바뀌어 뷰에서 추가로 정보를 요구하면 다른 값도 반환할 필요가 생겨요. 즉 DTO를 사용하는 뷰의 요구사항에 따라 DTO가 변경됨을 의미합니다.

 

이런 관점에서 범용적인 DTO에 대한 고민이 필요해 보입니다. 정말 딜러와 플레이어에 대한 정보를 뷰에서 보여줄 때 같은 내용으로 보여줄 것인가에 대한 고민이에요. 현재로서는 같고 변화할 가능성이 적다면 지금처럼 구현하는 것도 좋아요. 하지만 뷰 레이어는 수정이 빈번할 수 있기에 각 정보가 사용되는 곳에 맞게 DTO를 생성해 주면 좋습니다.

 

그렇기에 DTO를 생성하실 때 형태적인 측면보다 유즈케이스의 측면으로 접근하시는 편이 좋다고 생각합니다.

 

추후 웹 서버를 만들게 되면 이 부분이 더 와닿을 거예요. Post API의 경우 Body를 통해 정보를 받을 텐데 이 정보는 DTO에 담기게 되죠. Get API에서 정보를 응답할 때도 마찬가지고요. 각 API는 유즈케이스를 의미하므로 DTO를 분리하는 경험을 하실 겁니다.

 

DTO에 대해서 다시 한번 고민해 볼 수 있었고 좋은 인사이트를 얻어갈 수 있었던 리뷰였다.

마치며

이번 블랙잭 미션의 페어는 코코닥이었다. 페어가 결정되기 며칠 전 지인이 겹쳐 인사하러 갔다가 친해진 크루인데 페어가 되어 신기했다. 지인으로부터 워낙 잘한다는 말을 많이 들었던 크루였기에 두근거리면서 설렜다. 잘하는 크루와 함께한다는 것은 한편으로는 부담이 되지만, 우테코에서 중요한 것은 나의 기준을 세우고 나아가는 것을 깨달았기에 즐기면서 미션을 진행했던 것 같다. 처음으로 페어를 진행하면서 말을 놓게 되었는데 서로에 대한 이해가 높아진 상태에서 페어 프로그래밍을 진행하는 것이 훨씬 효율이 좋은 것을 직접 느낄 수 있었다. 앞으로도 페어가 동의한다면 계속해서 말을 놓은 편한 분위기에서 페어 프로그래밍을 진행해보고자 한다.

 

코코닥은 자바에 대한 이해도가 기본적으로 높고 배우고자 하는 의지가 강한 크루라는 것을 옆에서 직접 느낄 수 있었다. 변수명, 메서드명, 커밋 메시지 등 사소한 부분에 대해서도 깊은 고민을 통해 좋은 결과물을 만들어낼 수 있도록 노력했다. 나도 스스로 꼼꼼하다고 생각하는 편인데 훨씬 꼼꼼해서 좋은 결과물을 만들어낼 수 있었다고 생각한다.

 

설계 단계에서도 높은 집중력으로 더 좋은 방향으로 개선할 수 없을지 고민하였고 완성도를 갖춘 설계를 완성할 수 있었다. 설계가 탄탄하니 TDD를 진행하면서 테스트가 깨지는 개발을 하지 않았고, 메서드가 작은 기능을 가질 수 있게 개발을 마무리할 수 있었다. 자바를 학습한 지 얼마 되지 않았다고 했는데 앞으로의 성장이 너무 기대되는 크루였다. 지금도 막히는 부분이 있으면 코코닥에게 가서 얘기하며 좋은 인사이트를 얻어가곤 한다.

 

좋은 기회로 우테코에 들어올 수 있었고, 좋은 크루들과 함께 공부할 수 있어서 이번 연도가 계속해서 기대된다 🦋

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Total
Today
Yesterday