Skip to content

[🚀 사이클1 - 미션 (보드 초기화 + 기물 이동)] 아나키 미션 제출합니다.#215

Open
kcnsmoothie wants to merge 68 commits intowoowacourse:kcnsmoothiefrom
kcnsmoothie:step1
Open

[🚀 사이클1 - 미션 (보드 초기화 + 기물 이동)] 아나키 미션 제출합니다.#215
kcnsmoothie wants to merge 68 commits intowoowacourse:kcnsmoothiefrom
kcnsmoothie:step1

Conversation

@kcnsmoothie
Copy link
Copy Markdown

체크 리스트

  • 미션의 필수 요구사항을 모두 구현했나요?
  • Gradle test를 실행했을 때, 모든 테스트가 정상적으로 통과했나요?
  • 애플리케이션이 정상적으로 실행되나요?

어떤 부분에 집중하여 리뷰해야 할까요?

1. 테스트코드에 대한 고민

TDD를 수행하는 과정에서 문득 이런 의문이 들었습니다. 궁, 사는 이동 규칙이 같아서 이동하는 방법이 같습니다.
그로 인해 이를 검증하는 테스트 코드도 같아졌습니다. 이름만 다른 테스트 코드를 두 번씩이나 작성해야할까? 하는 의문이 듭니다.

2. 구조에 대한 고민

미션을 진행하면서 구조가 잘못되었다는 생각을 하게 되었습니다. 생각했던 초기 설계는 다음과 같습니다.

  • 초기에 보드판이 장기말의 위치를 관리하는 책임을 가지기로 했습니다.

  • 사용자에게 이동할 말과 이동하고자하는 위치를 입력 받습니다.

    어떤 기물을 옮기시겠습니까?(좌표와 기물을 입력하세요. 예시 : (0,0) 차)
    (1,1) 사
    
    어디로 옮기시겠습니까?(예시 : (0,5))
    (1,2)
    
  • 보드판에서 좌표에 해당 기물이 존재하는 지 검증합니다.

  • 존재한다면 해당 기물과 좌표, 이동할 좌표를 객체에게 넘겨서 이동할 수 있는지 검증합니다.

  • 이동할 수 있는지 여부를 boolean으로 보드판에 반환하고, true일 때 보드가 실제로 기물을 옮깁니다.

그러나 실제로 전략을 설계하면서 구조가 객체지향적이지 않고 의존적이라는 생각이 들었습니다. 그런 생각이 든 순간부터 멘탈이 완전히 무너졌고… 어떻게 해야했을까? 어떻게 고쳐야해야할까? 딜레마에 빠졌습니다.

구현하고자한 입출력도 구현하지 못하고, 기물 초기화와 이동 방식만 구현한 것 같습니다. 그 이후로는 돌아가는 대로 막 짜버린 것 같습니다. PR을 남기기 위해 글을 쓰면서 마음이 조금 진정된 상태인데요. 앞으로의 미션에 있어서도 막막함이 큰 상태입니다. 설계를 처음부터 꼼꼼히 하면 이런 문제를 해결할 수 있을까요?

Copy link
Copy Markdown

@Gomding Gomding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 아나키!
장기미션 함께하게된 찰리입니다 :)
고민이 깊어져 일단 제출하신것으로 이해했어요 😳
질문에 대한 답변은 남겼으니 애플리케이션 실행쪽 구현도 마무리해주세요~ 🙏

궁금한 점 있으면 편하게 DM 이나 코멘트 남겨주세요!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

애플리케이션이 정상적으로 실행되나요?

PR 본문에 해당 부분은 체크하셨는데
Application 실행과 입출력이 구현되지 않아서 구현 후 다시 요청해주세요! ☺️

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구현 완료했습니다!

Copy link
Copy Markdown

@Gomding Gomding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 아나키!
몇가지 수정해주셨는데 아직 동작에 부족함이 있네요 😢
코멘트 남겼으니 확인부탁드려요~

궁금한 점 있으면 언제든 DM 이나 코멘트 남겨주세요!


private final Map<Position, Piece> janggiBoard;

public JanggiBoard(Map<Position, Piece> janggiBoard) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JanggiController 에서 생성자에 비어있는 맵을 넣어주고 있는데
올바른 생성자 사용이 맞을까요 🤔
만약 비어있는 맵이 필요하다면 생성자 내부에서 초기화해줘도 된다고 생각해요

JanggiBoard janggiBoard = new JanggiBoard(new LinkedHashMap<>());


++) 가능하면 외부에서 장기판(Map)을 완성하고 생성자에 완성된 Map 을 넣어주는것은 어떻게 생각하시나요?
Board 가 어떤 흐름으로 초기화 되는지 Board 가 알게되면 너무 많은 정보를 알게될 것 같아요
특히 Board 의 초기화는 복잡한 과정이라 생각해요

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 피드백 감사합니다! 말씀하신 대로 장기에는 기물 배치에 따라 마상마상, 마상상마, 상마상마(오른상 차림), 상마마상 등 총 4가지의 초기 배치 케이스가 존재합니다.

현재는 가장 기본이 되는 배치를 우선 구현했지만, 향후 사용자가 원하는 차림을 선택할 수 있는 기능을 추가할 때 Map을 받는 형태가 변경에 더 유연한 구조가 될 것 같습니다.

Comment on lines +17 to +19
public void canMove(Position targetPosition) {
// moveStrategy.movable();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1.1단계 - 보드 초기화

게임 시작 시 장기판과 전체 기물을 올바른 위치에 초기화한다.
1.1단계에서는 기물의 이동은 구현하지 않는다.

1.2단계 - 기물 이동

각 기물의 이동 규칙을 구현한다.
기물의 이동 규칙은 직접 요구사항을 분석하여 정의한다.
궁성(宮城) 영역은 구현하지 않는다. (사이클2에서 다룬다)

기물 이동이 아직 불가능하네요 😢
요구사항은 기물 이동까지입니다
기물 이동까지 Application 에서 동작이 가능하게 구현해주세요~

Image

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각 기물은 이동 규칙을 가집니다. 이동 규칙에 따라 이동할 수 없는 좌표를 입력하면 이동하지 않도록 구현했습니다! 초기 구현과정에서는 예외 메시지를 던지는 부분까지 구현하지 않았었는데, 리팩터링하며 구현했습니다.

리뷰어님께 도움이 될까 싶어 정리해둔 규칙을 첨부합니다!

  - [x] 궁,사 : 방향에 관계 없이 한 칸씩 이동할 수 있다.
  - [x] 마 : 방향에 관계 없이 직진 후 대각선으로 한 칸 이동할 수 있다.
    - [x] 이동하는 경로에 기물이 존재해서는 안된다.
  - [x] 상 : 방향에 관계 없이 직진 후 대각선으로 두 칸 이동할 수 있다.
    - [x] 이동하는 경로에 기물이 존재해서는 안된다.
  - [x] 차 : 직선으로 원하는만큼 이동할 수 있다.
  - [x] 졸 : 좌,우,앞으로 한 칸씩 이동할 수 있다. 
    - [x] 뒤로는 갈 수 없다.
  - [x] 포 : 앞에 기물이 한 개 존재할 때 원하는 곳으로 뛰어넘을 수 있다.
    - [x] 포끼리는 뛰어넘을 수 없다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 제가 잘못 이동하고 있는거라면 말씀해주세요..

Image

0,3 -> 0,4
3,0 -> 4,0
0,6 -> 0,7
6,0 -> 7,0

4개 케이스 모두 이동이 안되고 있습니다 😢
턴이 문제인지 column,row 문제인지 시도해봤어요 😢 😢 😢 😢

int nextRows = next.getRows() + direction.getRowOffset();
int nextColumns = next.getColumns() + direction.getColOffset();

if (nextRows < 0 || nextRows >= 10 || nextColumns < 0 || nextColumns >= 9) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0, 10, 0 ,9 는 무엇을 의미하는 숫자인가요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

장기판은 9행, 10열로 구성되어있습니다. 그래서, 9와 10이라는 숫자가 CarStrategy 외에도 JanggiBoard에서도 재사용 되고 있습니다.

각 클래스에 private final 상수로 여러번 정의하기보다는 ENUM으로 관리하는 것이 좋을 것 같아서 Index라는 Enum으로 관리하도록 수정했습니다.

나름 생각을 가지고 리팩터링을 했지만, Enum을 지양하라는 조건이 있어서 이 방향이 맞는 걸까 의문이 남습니다. Enum을 지양해야하는 건 어떤 상황일까요?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum 을 지양하라는 조건이 있었나요?.. 😨

Comment on lines +23 to +35
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) {
return false;
}
Position position = (Position) o;
return rows == position.rows && columns == position.columns;
}

@Override
public int hashCode() {
return Objects.hash(rows, columns);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

equals, hashCode 는 어떤 의도로 재정의 해주셨는지 궁금해요!

Copy link
Copy Markdown
Author

@kcnsmoothie kcnsmoothie Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Position은 장기의 좌표값으로 사용되는 객체입니다. 장기에서 좌표는 고유한 값이라고 생각했습니다. Position(1, 1)이라는 객체가 메모리상의 주소는 다르더라도, 그 내부의 row와 col 값이 같다면 물리적으로 같은 위치를 나타내야 한다고 생각해서 equals, hashCode 를 재정의했습니다!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정확하게 이해하고 사용해주셨네요! 💯 💯 💯

Comment on lines +3 to +12
import domain.JanggiBoard;
import domain.piece.Cannon;
import domain.piece.Car;
import domain.piece.Elephant;
import domain.piece.Guard;
import domain.piece.Horse;
import domain.piece.King;
import domain.piece.Pawn;
import domain.position.Position;
import domain.piece.Piece;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DTO 를 만들어 주셨는데 view 는 domain 을 직접 의존하고있군요 🤔 🤔 🤔

아나키가 DTO 를 사용하신 이유는 무엇인가요?
DTO 가 탄생한 배경은 무엇일까요!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DTO를 도입하려했던 이유는 도메인 객체의 캡슐화를 유지하고 View와의 의존성을 분리하기 위함이었습니다. DTO 도메인을 알지 못하게 하고, 오직 출력에 필요한 데이터만 전달하려는 목적으로 생긴 것으로 알고 있습니다. 그러한 목적으로 DTO를 도입하고자 했는데 방향을 잃은 것 같습니다…

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정을 안하신 이유도 있을까요??...

Copy link
Copy Markdown

@Gomding Gomding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 아나키!
몇가지 코멘트 남겼으니 확인부탁드려요~

궁금한 점 있으면 언제든 DM 이나 코멘트 남겨주세요 :)

Comment on lines +17 to +19
public void canMove(Position targetPosition) {
// moveStrategy.movable();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 제가 잘못 이동하고 있는거라면 말씀해주세요..

Image

0,3 -> 0,4
3,0 -> 4,0
0,6 -> 0,7
6,0 -> 7,0

4개 케이스 모두 이동이 안되고 있습니다 😢
턴이 문제인지 column,row 문제인지 시도해봤어요 😢 😢 😢 😢

private void addPathCandidates(Position currentPosition, Direction direction, PieceProvider board, List<Position> candidatePositions) {
Position next = currentPosition;

while (true) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CarStrategy 와 관계가 있을까요???...

Comment on lines +3 to +12
import domain.JanggiBoard;
import domain.piece.Cannon;
import domain.piece.Car;
import domain.piece.Elephant;
import domain.piece.Guard;
import domain.piece.Horse;
import domain.piece.King;
import domain.piece.Pawn;
import domain.position.Position;
import domain.piece.Piece;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정을 안하신 이유도 있을까요??...

Direction[] straightDirections = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST};

for (Direction straight : straightDirections) {
Position myeok1 = new Position(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

도메인 용어 사용 👍 👍

int nextRows = next.getRows() + direction.getRowOffset();
int nextColumns = next.getColumns() + direction.getColOffset();

if (nextRows < 0 || nextRows >= 10 || nextColumns < 0 || nextColumns >= 9) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum 을 지양하라는 조건이 있었나요?.. 😨

Comment on lines +12 to +14
public List<Position> getMoveCandidates(Position from, PieceProvider board) {
List<Position> candidates = new ArrayList<>();
Direction[] straightDirections = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다.

  • 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.

함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.

  • 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라.

메서드의 라인수와 depth 규칙을 지킬 수 있게 리팩토링해주세요!

Comment on lines +16 to +18
for (Direction direction : directions) {
int targetRow = from.row() + direction.getRowOffset();
int targetColumns = from.col() + direction.getColOffset();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 이동을 못했던 이유가 Pawn 쪽 이동 버그때문이었군요 😓
졸은 진영에따라 이동 경로가 달라지니 그 부분을 신경써주세요~

import domain.piece.Piece;
import domain.position.Position;

public class JanggiGame {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 클래스는 어떤 목적으로 구현하신것일까요?

}

@Test
void 마가_목적지에_갈_수_있다() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이름이 잘못된 것 같아요!

assertThat(isCanMove).isFalse();
}

private static class TestPieceProvider implements PieceProvider {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestPieceProvider 가 중복으로 나타나고 있는데
테스트 픽스터라는 공유 클래스를 만들어보세요!

테스트 코드도 유지보수 대상입니다 :)
테스트 코드도 중복을 가능하면 제거하는게 좋아요~!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants