Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4529e0c
docs(gitignore): macOS .gitignore 초기 세팅
poketopa Mar 24, 2026
b54e335
test: 자동 생성된 PACKAGE_NAME 삭제
koreaioi Mar 24, 2026
24e0205
test: 페어 리모트 테스트
poketopa Mar 24, 2026
d9e796e
feat(domain): Point 생성 및 검증
poketopa Mar 24, 2026
2249919
feat(domain): Point 객체 equals / hashcode 오버라이딩 추가
poketopa Mar 24, 2026
102c9a9
refactor: 패키지 이동
koreaioi Mar 25, 2026
f917dfd
feat(domain): 기물 종류 정의 및 객체 생성
koreaioi Mar 25, 2026
a167ab1
refactor(domain): NonePiece는 소속된 팀이 없으므로, Team Null 처리
koreaioi Mar 25, 2026
f36482f
refactor(domain): Point 패키지 이동
koreaioi Mar 25, 2026
4bc50fd
feat(domain): 장기판 기물 배치 로직 추가
poketopa Mar 25, 2026
210b039
feat(domain): 빈 칸 정적 팩토리 메서드 추가
koreaioi Mar 25, 2026
74d0a0e
feat(domain): 상차림 초기화 구현
koreaioi Mar 25, 2026
d9f8370
chore(domain): 개행 컨벤션에 맞도록 수정
koreaioi Mar 25, 2026
ff15330
refactor(domain): 메서드 리팩토링
koreaioi Mar 26, 2026
a3e3d93
feat(domain): Point 축 기반 비교 로직 추가
poketopa Mar 26, 2026
e4f4709
feat(domain): Point의 다음 방향 이동을 구현
koreaioi Mar 26, 2026
51e3613
feat(domain): Point 위치 이동 검증
koreaioi Mar 26, 2026
3f01333
feat(domain): 주어진 경로를 순차적으로 이동 시, 최종 목적지에 도달하도록 구현
koreaioi Mar 26, 2026
2c4084b
refactor(domain): direction을 한단계 낮은 vector로 네이밍 수정
koreaioi Mar 26, 2026
69e688f
feat(domain): 주어진 경로의 좌표들을 변환하는 로직 추가
poketopa Mar 26, 2026
cb7c525
feat(domain): 마 이동 가능 여부를 구현
koreaioi Mar 27, 2026
21321cd
feat(domain): 상 이동 가능 여부를 구현
poketopa Mar 27, 2026
e5ffff4
feat(domain): 차 이동 가능 여부를 구현
poketopa Mar 27, 2026
87c8986
feat(domain): 포 이동 가능 여부를 구현
koreaioi Mar 27, 2026
e55c900
feat(domain): 졸병 이동 가능 여부를 구현
koreaioi Mar 27, 2026
004fe1d
feat(domain): 장군 이동 가능 여부를 구현
koreaioi Mar 27, 2026
1fe72f6
refactor(domain): 졸병 변수명 수정
koreaioi Mar 27, 2026
af00787
feat(domain): 사 이동 가능 여부를 구현
koreaioi Mar 27, 2026
d5732e3
feat(domain): 전체 이동을 구현
koreaioi Mar 27, 2026
9c3417b
chore: 린팅 적용
poketopa Mar 27, 2026
93447f8
docs(README): 리드미 업데이트
poketopa Mar 27, 2026
a78b294
refactor(board): 삼항 연산자 제거
poketopa Mar 27, 2026
991559d
refactor(domain): 삼항연산자 제거
poketopa Mar 28, 2026
45f0dc6
chore: 주석제거
poketopa Mar 28, 2026
146434b
feat(domain): PieceType 한자 필드 추가
poketopa Mar 29, 2026
4800bea
refactor(domain): Team 패키지 분리 및 한글, 한자 필드 추가
poketopa Mar 29, 2026
68ccf14
feat(Application): 프로그램 시작 도입부 생성
poketopa Mar 29, 2026
b8b5f9d
feat(controller): 장기 컨트롤러 생성
poketopa Mar 29, 2026
3310d18
feat(dto): 입출력 DTO 생성
poketopa Mar 29, 2026
d70139a
feat(view): 입출력 뷰 추가
poketopa Mar 29, 2026
c0577ba
chore: 전체 코드 린팅 적용
poketopa Mar 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,12 @@ out/

### VS Code ###
.vscode/

### macOS ###
.DS_Store
.AppleDouble
.LSOverride
Icon?
._*
.Spotlight-V100
.Trashes
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,75 @@
# java-janggi
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

애플리케이션을 직접 실행해보니 범위 밖 좌표를 입력했을 때 화면에 "null"이 출력됩니다.
사용자한테 "null"이 보이면 뭐가 잘못된 건지 알 수가 없으니, 예외 메시지를 넣어주면 어떨까요?

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.

잘못된 좌표가 입력되었을 때 에러 메시지가 출력되도록 수정했습니다.

private void validate(int y, int x) {
        if (checkPointRange(y, x)) {
            return;
        }
        throw new IllegalArgumentException("잘못된 좌표 입력입니다.");
    }


장기 미션 저장소

### 기능 요구사항 체크리스트

#### step 1
- [x] 10x9 장기판 좌표를 `Point(y, x)`로 표현하고, 범위를 벗어난 좌표 생성 시 예외를 발생시킨다.
- [x] 기물 타입(`PieceType`)과 진영(`Team`)을 분리해 도메인 모델링한다.
- [x] 빈 칸을 `NonePiece`로 포장해 `Intersection.empty()`로 관리한다.
- [x] 장기판 초기화 시 전체 좌표를 빈 칸으로 채운 뒤, 생성기(`IntersectionGenerator`)가 전달한 기물을 배치한다.
- [x] 초/한 기본 배치(졸, 포, 장군, 사, 차)를 자동 생성한다.
- [x] 상/마 차림(`Formation`) 4가지를 지원하고, 진영별로 대칭 배치한다.
- [x] `Formation.valueOf(int)`로 입력 숫자를 차림 enum으로 변환한다.
- [x] 보드에서 좌표로 교차점(`Intersection`)을 조회할 수 있다.
- [x] 이동 규칙을 `MoveRule` 전략으로 분리하고, 기물 타입별 규칙 클래스를 연결한다.
- [x] 이동 경로를 `Direction`/`Directions`로 추상화해 “도착 가능 여부 + 경유 좌표”를 계산한다.
- [x] 차(`Chariot`)는 상하좌우 직선 이동만 가능하며, 경로 중간 장애물이 있으면 이동할 수 없다.
- [x] 마(`Horse`)는 2단계 경로(멱) 중 중간 칸이 막히면 이동할 수 없다.
- [x] 상(`Elephant`)은 3단계 경로 중 중간 칸이 막히면 이동할 수 없다.
- [x] 포(`Cannon`)는 정확히 1개의 장애물을 넘어야 하며, 포를 넘거나 포를 공격할 수 없다.
- [x] 장군(`General`)과 사(`Guard`)는 1칸 상하좌우 이동을 지원한다.
- [x] 졸(`Soldier`)은 좌/우 + 전진 이동을 지원하며, 진영에 따라 전진 방향이 다르다.
- [x] 같은 팀 기물이 있는 칸으로는 이동할 수 없다.
- [x] `JanggiBoard.tryToMove()` 수행 시 도착지는 출발 기물로 갱신되고, 출발지는 빈 칸이 된다.

### 주요 로직 요약

#### step 1
- **보드 생성**
- `JanggiBoard`가 10x9 전체 좌표를 `Intersection.empty()`로 초기화한다.
- **초기 기물 배치**
- `JanggiGenerator`가 진영(`CHO`, `HAN`)별 기본 행/열 규칙으로 기물을 만든다.
- 상/마는 `Formation`에 정의된 열 인덱스 조합으로 배치한다.
- **좌표/교차점 모델링**
- `Point`는 생성 시 범위 검증을 수행하고, `next(Vector)`로 이동 좌표를 계산한다.
- `Intersection`은 기물 도착(`arrive`)과 이탈(`leave`) 책임을 가진다.
- **이동 규칙 선택**
- 보드는 출발 지점의 기물 타입을 기준으로 `MoveRule` 구현체를 선택한다.
- **경로 계산**
- 규칙별 `Directions.findPoints(from, to)`로 목적지까지의 경유 좌표 목록을 만든다.
- **규칙 검증**
- 공통적으로 같은 팀 도착지 여부를 검증한다.
- 차/마/상은 경로 장애물 조건을 검증한다.
- 포는 “장애물 1개 필수”, “포 넘기/포 공격 금지”를 추가 검증한다.
- **실제 이동 반영**
- 검증 통과 시 도착지 `arrive(from)`, 출발지 `leave()` 순서로 상태를 갱신한다.

### 기물별 이동 규칙 정리

- **차(`Chariot`)**
- 상/하/좌/우 직선 다칸 이동
- 도착지 아군 금지
- 경로 중간 장애물 금지
- **포(`Cannon`)**
- 상/하/좌/우 직선 이동
- 중간 장애물 정확히 1개 필요
- 장애물/도착지가 포인 경우 금지
- 도착지 아군 금지
- **마(`Horse`)**
- 1칸 직선 + 1칸 대각(총 2스텝)
- 중간 경유 칸 장애물 금지
- 도착지 아군 금지
- **상(`Elephant`)**
- 1칸 직선 + 2칸 대각(총 3스텝)
- 경유 칸 장애물 금지
- 도착지 아군 금지
- **장군/사(`General`/`Guard`)**
- 1칸 상하좌우 이동
- 도착지 아군 금지
- (궁성 내부/대각 특수 규칙 미반영)
- **졸(`Soldier`)**
- 좌/우 + 전진 이동
- CHO는 위쪽(UP), HAN은 아래쪽(DOWN) 전진
- 도착지 아군 금지
16 changes: 16 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import controller.JanggiController;
import view.InputView;
import view.OutputView;

public class Application {

public static void main(String[] args) {
InputView inputView = new InputView();
OutputView outputView = new OutputView();

JanggiController janggiController = new JanggiController(inputView, outputView);
janggiController.run();

}

}
41 changes: 41 additions & 0 deletions src/main/java/controller/JanggiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package controller;

import domain.board.Formation;
import domain.board.JanggiBoard;
import domain.board.JanggiGenerator;
import domain.game.Game;
import domain.team.Team;
import dto.MoveDTO;
import view.InputView;
import view.OutputView;

public class JanggiController {

private final InputView inputView;
private final OutputView outputView;

public JanggiController(InputView inputView, OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}

public void run() {
Formation hanFormation = Formation.valueOf(inputView.inputHanWingSetup());
Formation choFormation = Formation.valueOf(inputView.inputChoWingSetup());
JanggiGenerator janggiGenerator = new JanggiGenerator(hanFormation, choFormation);

Game game = new Game(new JanggiBoard(janggiGenerator));

while (true) {
try {
outputView.printCurrentBoardStatus(game.boardStatus());
final Team turn = game.currentTurn();
outputView.printCurrentTurn(turn);
MoveDTO move = new MoveDTO(inputView.inputMovePiecePoint(), inputView.inputDestinationPoint());
game.processTurn(move);
} catch (IllegalArgumentException e) {
outputView.printErrorMessage(e.getMessage());
}
}
Comment on lines +29 to +39
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

game 승리 조건이 생기면 바뀔 수도 있긴 하겠지만, 얘기해보면 좋을 내용이라 남겨봅니다 :

while (true) 는 위험한 로직이에요.
지금 당장 문제를 일으키지 않더라도, 다른 변경사항으로 인해 의도치 않게 종료되지 않는 프로그램이 될 수 있기 때문인데요.

그래서 웬만하면 명시적인 종료 조건을 while 문에 넣는 것을 권장해요.
예를 들어 while (!game.isFinished()) 처럼요.
지금은 승리 판단이 없더라도, 자리를 만들어두면 나중에 로직만 채우면 됩니다.

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.

승리로직을 간단하게 구현하여 while (true)로직을 없앴습니다!

왕이 모두 살아있지 않은 상황을 턴마다 확인하도록 했습니다.

public boolean isGameRunning() {
        int generalCount = 0;
        for (Intersection intersection : intersections.values()) {
            if (intersection.isSamePiece(PieceType.GENERAL)) {
                generalCount++;
            }
        }
        if (generalCount == 2) {
            return true;
        }
        return false;
    }

다만, 승리를 확인한 뒤에도 다음 턴으로 넘어가는 로직은 수행되기에 승자를 표시하기 위해서는 역보정을 해야했고,
이것이 불안정하고 직관적이지 않은 구조라고 생각되어 사이클 2에서 리팩토링 해보겠습니다!!

public void processTurn(MoveDto move) {
        Point from = move.getFrom();
        Point to = move.getTo();

        checkCurrentTurnTeam(from);
        janggiBoard.tryToMove(from, to);
        isGameRunning = janggiBoard.isGameRunning();
        turn = turn.nextTurn();
    }


final Team winnerTeam = game.currentTurn().nextTurn();
        outputView.printWinnerTeam(winnerTeam);

}
}
39 changes: 39 additions & 0 deletions src/main/java/domain/board/Formation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package domain.board;

import java.util.Arrays;
import java.util.List;

public enum Formation {

HORSE_ELEPHANT_HORSE_ELEPHANT(1, List.of(2, 7), List.of(1, 6)),
HORSE_ELEPHANT_ELEPHANT_HORSE(2, List.of(2, 6), List.of(1, 7)),
ELEPHANT_HORSE_ELEPHANT_HORSE(3, List.of(1, 6), List.of(2, 7)),
ELEPHANT_HORSE_HORSE_ELEPHANT(4, List.of(1, 7), List.of(2, 6)),
;

private final int formatNumber;
private final List<Integer> elephantFormations;
private final List<Integer> horseFormations;

Formation(int formatNumber, List<Integer> elephantFormations, List<Integer> horseFormations) {
this.formatNumber = formatNumber;
this.elephantFormations = elephantFormations;
this.horseFormations = horseFormations;
}

public static Formation valueOf(int formatNumber) {
return Arrays.stream(Formation.values())
.filter(formation -> formation.formatNumber == formatNumber)
.findFirst()
.orElseThrow(IllegalArgumentException::new);
}

public List<Integer> elephantFormations() {
return elephantFormations;
}

public List<Integer> horseFormations() {
return horseFormations;
}

}
10 changes: 10 additions & 0 deletions src/main/java/domain/board/IntersectionGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package domain.board;

import domain.intersection.Intersection;
import java.util.List;

public interface IntersectionGenerator {

List<Intersection> makeIntersection();

}
104 changes: 104 additions & 0 deletions src/main/java/domain/board/JanggiBoard.java
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.

장기 미션을 수행하면서 왜 이렇게 불변 객체에 대한 언급이 많을까? 하는 의문이 들었습니다.

불변 객체의 사용 이유들을 찾아보며 공부했고, 제가 생각하게 된 불변 객체 사용의 이유는 잘못된 동작의 책임을 동작 로직이 아니라 생성 자체로 확정지을 수 있다는 것이었습니다.

예시로, 어떤 기물의 동작이나 생성이 잘못되었을 때 게임이 진행되는 과정에서 그 영향이 확산되어 어떤 지점에서 잘못된 동작이 발생했는지 발견하기 힘들 수 있습니다.

하지만, 장기 관련 객체가 모두 불변이라면 특정 기물이 잘 못 생성되었다.라는 정보를 확실하게 알 수 있다는 것입니다.

과연 그렇다면 왜 모든 객체를 불변으로 사용하지 않는가? 하는 질문이 생깁니다.

JVM의 GC는 강력하기에 메모리 점유는 문제가 되지 않고, 장기 미션 수준에서는 모든 객체를 불변으로 설정한다.라는 조건을 충족하는 것이 충분히 가능할테니까요.

그럼에도 불변 객체의 사용을 선택으로 남겨놓는 것은 현업에서는 모든 값을 불변으로 사용한다는 것 자체가 매우 힘들기 때문인 것인지, 그 수준에서는 메모리 낭비에 대한 압박이 있는 것인지, 혹은 그저 취향 차이인 것인지에 대한 궁금증이 있습니다.

이에 대한 피케이의 의견이 궁금합니다!

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

Choose a reason for hiding this comment

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

Q. 불변은 취향인가 오버헤드인가 필수인가
Direction 하드코딩도 그렇고 꽤나 재밌는 도전 및 의문을 가지시는군요!
일단 취향 차이는 분명히 아니고, 말해주신 것처럼 이 정도 규모에서는 메모리 압박이 그리 크지 않습니다.

제가 생각했을 때 장기게임에서 불변이 선택사항인 이유는 크게 두 가지입니다 :

  • 아직 그정도로 안 만들어도 되기 때문에
  • 게임은 그 특성상 상태변화가 너무나도 당연하고, 그만큼 자주 일어나기 때문에

상태변화가 자주 일어나는 것들을 어떻게든 불변으로 만들면
얻는 이점보다 불편한 점이 더 크다는 것을 느낄 때가 있습니다.
코드도 복잡해지고, 일관성을 챙기기도 어려운데 그에 비해 실질적으로 느끼는 장점이 많지 않은거죠.

즉 불변은 '모든 것을 불변으로 만들겠다'는 생각으로 만든다기 보단,
'상태변화가 특징인 것들은 불변으로 만들지 않고, 그 외엔 불변 객체로 둬서 그 이점을 챙기겠다'는 생각으로 만드는 것 같네요. 이점은 루덴스가 잘 설명해주신 듯 하고요.

실제 개발을 할 때도, 그리고 더 나아가 Java 와 Spring 진영 로직들도 다 비슷한 생각으로 만들어집니다.
그래서 제 결론은 '굳이 모든 객체를 불변으로 만들 필요가 있을까? 필요에 의해 가변 객체를 만들 수도 있지' 입니다.

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.

피케이의 생각 들려주셔서 감사합니다!

처음엔 불변 객체를 사용하는 이유가 개발자의 실수를 막기 위해서 라는 것이 이해가 잘 안됐습니다.

불변으로 만들어 놓으면 수정이 안되니까 실수를 사전에 방지한다? 그렇다면 new로 만들어질 때 실수하는 것도 똑같이 위험하지 않은가? 하는 질문이 계속 생겼거든요.

지금은 그럼에도 불변 객체를 만드는 것이 좋은 이유를 구체적으로 알아가고 있지만,

잘못된 동작의 책임을 동작 로직이 아니라 생성 자체로 확정지을 수 있다는 것 등

그럼에도 조금의 모호함이 느껴지는 것 같습니다.

피케이의 코멘트에서도 조금의 의문이 생기는데요,

그래서 제 결론은 '굳이 모든 객체를 불변으로 만들 필요가 있을까? 필요에 의해 가변 객체를 만들 수도 있지' 입니다.

여기서 필요라는 것은 불변으로 만들기 위해 소비될 코드를 작성하는 시간, 바뀌어야 하는 코드, 사람의 스트레스 등 > 객체를 불변으로 만듦으로써 얻는 안정성인 상황인걸까요?

혹은 정말 도저히 불변을 사용할 수 없는 상황을 가정하는 것일까요?

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package domain.board;

import domain.intersection.Intersection;
import domain.piece.move.CannonMoveRule;
import domain.piece.move.ChariotMoveRule;
import domain.piece.move.ElephantMoveRule;
import domain.piece.move.GeneralMoveRule;
import domain.piece.move.GuardMoveRule;
import domain.piece.move.HorseMoveRule;
import domain.piece.move.MoveRule;
import domain.piece.move.SoliderMoveRule;
import domain.point.Point;
import dto.BoardStatusDTO;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class JanggiBoard {

private static final int MAX_ROW = 10;
private static final int MAX_FILE = 9;

private final Map<Point, Intersection> intersections;
private final List<MoveRule> moveRules;

public JanggiBoard(IntersectionGenerator intersectionGenerator) {
this.intersections = fillEmptyIntersections();
this.moveRules = setMoveRules();
for (Intersection intersection : intersectionGenerator.makeIntersection()) {
intersections.put(intersection.getPoint(), intersection);
}
}

private static Stream<Point> getAllPoints() {
return range(MAX_ROW).boxed()
.flatMap(row -> range(MAX_FILE).mapToObj(f -> new Point(row, f)));
}

private static IntStream range(int maxRange) {
return IntStream.range(0, maxRange);
}

public void tryToMove(Point start, Point end) {
Intersection from = findIntersection(start);
Intersection to = findIntersection(end);

validateMoveRule(from, to);
move(to, from);
}

private void move(Intersection to, Intersection from) {
to.arrive(from);
from.leave();
}

private void validateMoveRule(Intersection from, Intersection to) {
MoveRule moveRule = findMoveRule(from);
List<Point> possiblePoints = moveRule.findPossiblePoints(from, to);
List<Intersection> path = findPath(possiblePoints);
moveRule.checkMoveRule(from, path);
}

public MoveRule findMoveRule(Intersection from) {
return moveRules.stream()
.filter(moveRule -> moveRule.support(from))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("선택한 좌표에 이동 가능한 기물이 없습니다."));
}

private List<Intersection> findPath(List<Point> possiblePoints) {
return possiblePoints.stream()
.map(this::findIntersection)
.toList();
}

public BoardStatusDTO boardStatus() {
return new BoardStatusDTO(intersections);
}

public Intersection findIntersection(Point point) {
return intersections.get(point);
}

private List<MoveRule> setMoveRules() {
return List.of(
new ChariotMoveRule(),
new GeneralMoveRule(),
new GuardMoveRule(),
new ElephantMoveRule(),
new SoliderMoveRule(),
new CannonMoveRule(),
new HorseMoveRule()
);
}

private Map<Point, Intersection> fillEmptyIntersections() {
return getAllPoints()
.collect(Collectors.toMap(point -> point, Intersection::empty));
}
Comment on lines +98 to +101
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

일부 클래스의 메서드 순서가 조금 뒤섞여 있어요.
public -> private, 또는 호출 순서대로 배치하는 등 본인만의 컨벤션을 만들어보면 어떨까요?
파일이 길어질수록 일관된 순서가 가독성에 큰 차이를 만들어요.
관리하는 개발자가 많아질 때 특히 유용하고요!

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.

컨벤션을 확인하고 필요한 부분에 반영했습니다!

우선순위:
1순위 - 필드
2순위 - 생성자
3순위 - 생성자 전용 private
4순위 - public 메서드 

클린코드에서 신문 기사처럼 작성하라는 지침이 있어서 읽어보고 참고했습니다!



Comment on lines +102 to +103
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
Author

Choose a reason for hiding this comment

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

수정했습니다!

}
Loading