<우아한 테크코스 5기 프리코스 진행사항 - 4주차>
4주차 회고가 작성이 많이 늦어졌다. 1~3주차까지 많은 어려움이 있었지만 해결되는 과정에 큰 배움을 느꼈다면,
4주차의 과제는 모든 어려움의 집합체였다. 막막하기도 하였고, 다리 건너는 과정을 print문으로 보여주기도 해야해서
기능으로 구현할 것이 한 둘이 아니었다. 미뤄놓고 가끔 찾아보며 코드를 리팩토링해보기도 하였지만
프리코스를 진행할 당시의 느낀점과 그 코드를 토대로 회고를 하는게 맞다고 생각되어 작성하게 되었다.
4주차에서는 객체지향설계를 지향하는만큼 게임을 관리하는 클래스와, 각 클래스에서 기능적으로 원활한 코드를 작성하는 것이 우선적인 목표였다.
1) 기능 요구 사항
2) 프로그래밍 요구 사항
3) 과제 진행 요구 사항
<백엔드 - Java> 기준으로 과제를 수행하였다.
<다리 건너기>
- 기능 요구 사항
- 입출력 요구 사항
- 프로그래밍 요구 사항
와 OutputView 클래스 / BridgeGame 클래스 / BridgeMaker 클래스 / BridgeRandomNumberGenerator 클래스 등에서
기본적으로 작성되어야할 기본 코드가 제공되며,
우아한 테크코스에서 제공하는 라이브러리 사용을 권장한다. camp.nextstep.edu.missionutils
Ex) 입력값
- 사용자가 입력하는 값은 camp.nextstep.edu.missionutils.Console의 readLine()을 활용한다.
- 구현과정
3주차와 마찬가지로 요구사항을 살펴보니 "indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현 (2까지만 허용)" 이 있었다. 함수를 분리하여 구현하는 것이 중요해보였다.
- bridge라는 패키지를 만들어, 각 클래스를 관리하고 실행할 Application.java
- 총 게임을 관리하고 각 클래스를 호출하고 값을 받아오는 BridgeGame.java
- 다리의 생성을 담당하는 BridgeMaker.java
- 다리의 숫자 생성에서 인터페이스 역할을 하는 BridgeNumberGenerator.java
- 다리를 건널 때 위와 아래로 이동하는 경우를 숫자 상수로 지정해야하는 요구사항을 지키기 위해,
private static final int RANDOM_LOWER_INCLUSIVE = 0; 와 RANDOM_UPPER_INCLUSIVE = 1;
로 지정할 BridgeRandomNumberGenerator.java
- 사용자에게 값을 입력받을 InputView.java
- 처리한 모든 내용을 화면 상에 출력할 OutputView.java 까지 각 클래스별로 나눌 수 있었다.
<Application.java>
package bridge;
public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
final BridgeGame bridgeGame = BridgeGame.create();
bridgeGame.move();
}
}
- Application.java에서 BridgeGame을 만들고 create()와 move()를 호출하여 게임을 진행할 수 있도록 하였다.
<BridgeGame.java>
package bridge;
import java.util.List;
/**
* 다리 건너기 게임을 관리하는 클래스
*/
public class BridgeGame {
static int bridge_size = InputView.readBridgeSize();
static String final_result;
static int final_count;
static String map_move;
BridgeGame(){
}
public static BridgeGame create(){
return new BridgeGame();
}
/**
* 사용자가 칸을 이동할 때 사용하는 메서드
* <p>
* 이동을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
*/
public void move() {
String direction = InputView.readMoving();
List<String> Bridge_path = BridgeMaker.makeBridge(bridge_size);
int i = 0; int count = 0;
while(i == (bridge_size*2)) { // 끝까지 도달할때까지 반복
if (direction == "U") { // "U"을 입력하였고, 정답이면, 다음칸으로
if (Bridge_path.get(i) == direction) { i += 2; count++; map_print("O"); direction = InputView.readMoving();
} else map_print("X"); retry();
} else if (direction == "D") { // "D"를 입력하였고, 정답이면, 다음칸으로
if (Bridge_path.get(i) == direction) { i += 2; count++; map_print("O"); direction = InputView.readMoving();
} else map_print("X"); retry();
}
}
if(i >= (bridge_size*2)) success("성공", count);
}
public static void success(String result, int count){
final_result = result;
final_count = count;
}
public static void map_print(String is_success){
map_move = is_success;
}
public static String map_move(){
return map_move;
}
public static String endgame_1(){
return final_result;
}
public static int endgame_2(){
return final_count;
}
/**
* 사용자가 게임을 다시 시도할 때 사용하는 메서드
* <p>
* 재시작을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
*/
public void retry() {
String game_choice = InputView.readGameCommand();
boolean result = true; // 성공
if(game_choice == "R") create();
else if(game_choice == "Q") success("실패", final_count);
}
}
가장 많은 기능들을 작성한 클래스였다. 더 분리해서 관리할 수 있었지만 작성하다보니 담당할 부분이 너무 많아지기도했다.
먼저 InputView.java로 부터 다리의 길이를 입력받고, bridge_size변수로 관리한다.
create()메서드에서는 계속해서 새로운 게임을 시작할 BridgeGame을 리턴시킨다.
move()메서드에서는 실질적으로 이동하는 기능을 구현해야했는데, direction으로 방향을 지정하고, Bridge_path를 통해 경로를 기억해놔야했다.
그리고 다리의 길이 2배만큼 반복하는동안 지속적으로 방향(direction)을 체크해야했다.
X가 입력되면 retry()를 호출한다.
map_print(), map_move(), endgame_1(), endgame_2() 처럼 굳이 필요치 않았던 메서드가 추가되기도했다.
retry()메서드에서는 게임을 다시시작하는 것을 담당하는데,
InputView.readGameCommand()를 통해 사용자의 의사를 입력받아오고
R이 입력될 경우 create()메서드를 호출하여 게임을 재시작하고,
Q일 경우에는 "실패"를 출력하고 지금까지 진행해온 값을 출력한다.
하나하나 기능을 생각하며 구현해봤지만 모두 예상처럼 작동하지 않았다. 효율적이지 않은 코드로 하드코딩한 것 같았다.
<BridgeMaker.java>
package bridge;
import java.util.ArrayList;
import java.util.List;
import bridge.BridgeRandomNumberGenerator;
/**
* 다리의 길이를 입력 받아서 다리를 생성해주는 역할을 한다.
*/
public class BridgeMaker {
private final BridgeNumberGenerator bridgeNumberGenerator;
public BridgeMaker(BridgeNumberGenerator bridgeNumberGenerator) {
this.bridgeNumberGenerator = bridgeNumberGenerator;
}
/**
* @param size 다리의 길이
* @return 입력받은 길이에 해당하는 다리 모양. 위 칸이면 "U", 아래 칸이면 "D"로 표현해야 한다.
*/
public static List<String> makeBridge(int size) {
List<String> bridge = new ArrayList<>();
int temp; String correct_direction;
for(int i = 0; i < size; i++){
temp = BridgeRandomNumberGenerator.generate_num();
if(temp == 1) {
correct_direction = "U";
bridge.add(i, correct_direction); }
else if(temp == 0) {
correct_direction = "D";
bridge.add(i, correct_direction); }
} return bridge;
}
}
다리를 생성하는 부분이다. temp변수에 BridgeRandomNumberGenerator.generate_num()부분으로 입력받아와
1이면 Up 기능을하여 bridge 문자열 리스트에 그 값을 추가하고,
0이면 Down 기능을 하여 bridge 문자열 리스트에 그 값을 추가한다.
반복문이 끝나면 그 리스트를 반환하는 메서드이다. (makeBridge(int size))
<BridgeRandomNumberGenerator.java>
package bridge;
import camp.nextstep.edu.missionutils.Randoms;
public class BridgeRandomNumberGenerator implements BridgeNumberGenerator {
// 아래를 0으로 지정
private static final int RANDOM_LOWER_INCLUSIVE = 0;
// 위를 1로 지정
private static final int RANDOM_UPPER_INCLUSIVE = 1;
// 위(1) or 아래(0) 중 하나를 반환
@Override
public int generate() {
return Randoms.pickNumberInRange(RANDOM_LOWER_INCLUSIVE, RANDOM_UPPER_INCLUSIVE);
}
public static int generate_num(){
return Randoms.pickNumberInRange(RANDOM_LOWER_INCLUSIVE, RANDOM_UPPER_INCLUSIVE);
}
}
방향을 상수로 표현하여 그 값을 토대로 이동하고 랜덤 번호를 선택하는 메서드이다.
<InputView.java>
package bridge;
import camp.nextstep.edu.missionutils.Console;
/**
* 사용자로부터 입력을 받는 역할을 한다.
*/
public class InputView {
/**
* 다리의 길이를 입력받는다.
*/
public static int readBridgeSize() {
System.out.println("다리의 길이를 입력해주세요. ");
String input = Console.readLine();
int bridge_length = Integer.parseInt(input);
BridgeMaker.makeBridge(bridge_length);
return bridge_length;
}
/**
* 사용자가 이동할 칸을 입력받는다.
*/
public static String readMoving() {
System.out.println("이동할 칸을 선택해주세요. (위: U, 아래: D)");
String direction = Console.readLine();
return direction;
}
/**
* 사용자가 게임을 다시 시도할지 종료할지 여부를 입력받는다.
*/
public static String readGameCommand() {
System.out.println("게임을 다시 시도할지 여부를 입력해주세요. (재시도: R, 종료: Q)");
String game_choice = Console.readLine();
return game_choice;
}
}
사용자로부터 다리의 길이, 이동할 칸 (U or D), 게임을 다시 시도할지의 여부를 입력받아
각 입력 값에 맞게 각각의 클래스들을 호출한다.
입력값처리는 요구된 라이브러리를 사용해야했기에
System.out.println("다리의 길이를 입력해주세요. ");
String input = Console.readLine();
처럼 처리되었다. Console.readLine()은 기본적으로 String으로 입력받기 때문에 bridge_length로 변환하기 위해서
Integer.parseInt(input)을 해주어 정수형 변환 후 bridge_length로 저장하였다.
변환한 다리의 길이 변수(bridge_length)를 BridgeMaker 클래스의 makeBridge() 함수에 bridge_length 인자로 전달한다.
readMoving()과 readGameCommand()도 유사하게 각 클래스에 인자로 전달 후 값을 반환받았다.
<OutputView.java>
package bridge;
/**
* 사용자에게 게임 진행 상황과 결과를 출력하는 역할을 한다.
*/
public class OutputView {
/**
* 현재까지 이동한 다리의 상태를 정해진 형식에 맞춰 출력한다.
* <p>
* 출력을 위해 필요한 메서드의 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
*/
public void printMap() {
String move_print = BridgeGame.map_move(); int count = BridgeGame.endgame_2();
String count_print = " | " + move_print;
for(int i = 0; i <= count; i++){
if(count == 1){
System.out.println("[ " + move_print + " ]"); System.out.println("[ " + move_print + " ]");
}
else if(count >= 1){
System.out.println("[ " + move_print + count_print + " ]");
System.out.println("[ " + move_print + count_print + " | " + move_print + " ]");
}
}
}
/**
* 게임의 최종 결과를 정해진 형식에 맞춰 출력한다.
* <p>
* 출력을 위해 필요한 메서드의 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
*/
public void printResult() {
String final_result = BridgeGame.endgame_1();
int final_count = BridgeGame.endgame_2();
System.out.println("\n게임 성공 여부: " + final_result);
System.out.println("총 시도한 횟수: " + final_count);
}
}
가장 까다롭고 어떻게 구현해야할지 고민이 많았던 클래스였다. 결국 출력을 담당하는 클래스에서 요구되는 기능에 맞게 출력하진 못했지만, 이러한 흐름이면 괜찮지 않을까하는 리팩토링 과정이 많았다.
<Commit Log>
- [Add] <Init_Game, InputView> Connect Class
- [Add] <BridgeGame> All Variable Declaration, [Refactor] <InputView>
- [Add] <BridgeMaker> Connect BridgeRandomNumberGenerator
- [Add] <OutputView> Print present_map, Print final_result
- [Refactor] <InputView> Input Bridge_size, readMoving, (if End the game, Input Quit or Retry Command)
- [Refactor] <OutputView> Print Present_map
<과제 수행 후기>
이번 미션을 진행해보면서 객체지향설계의 지향점에 대해서 코드단으로 느껴볼 수 있었습니다.
게임을 관리하는 클래스와 각 게임별 변수를 선언하는 클래스 등 지난 1~3주차를 진행해오면서 점차 고도화되어가는 클래스의 수준을 알 수 있었습니다. 완벽히 구현해내거나 기능적으로 원활한 코드를 작성하는 것이 프로그래밍의 최종 목표이지만, 우테코를 통해 1~4주차 과제를 진행해오면서 지금까지 한번도 접해보지 못했던 방식으로 코드를 작성하는 습관을 들여야겠다고 생각했습니다. 우테코를 통해 많이 성장할 수 있었던 점은 커뮤니티도 있지만 과제에 있어서 스스로 해결할 수 있도록 기술적 요구사항을 상세히 기술해주시고 자기주도적으로 학습할수있도록 이끌어주신 멘토님들의 능력이었던 것 같습니다.
아직은 많이 부족하지만 약 한 달간의 과정을 통해서 제 스스로가 코드 리팩토링, 코드 컨벤션 등 새롭게 배운 부분이 많았습니다.
IntelliJ 사용에 익숙치 않아 Eclipse로 코드를 작성 후 IntelliJ로 옮기는 과정을 많이했었는데,
이번에도 우아한 테크코스에서 제공하는 라이브러리 camp.nextstep.edu.missionutils 은 Eclipse에서는 외부라이브러리를 Import하지 않는한 인식하지 못하였기때문에 그 부분에서 오류가 생긴게 아닐까 싶다.
IntelliJ에 정상적으로 코드를 이식한 후에도 IntelliJ의 JDK버전을 설치하는 문제나, IDE상에는 잘 출력이되어도 과제 제출 페이지에서는 이와 같은 오류발생이 나오게 되어 과제 제출 5분전까지도 진땀흘리며 고치려고 했던 경험이 있다.
최종 제출 결과는 "예기치 못한 오류로 인하여 실행에 실패하였습니다" 였지만, 과제를 수행해오면서 배운 점이 더 많았다.
이러한 어려움이 내 개발 커리어에서 성장의 원동력이 되기를 바라며 프리코스를 수행하는 것이 매우 보람찼다.
아쉬운 결과였지만 비슷한 알고리즘 문제를 더 많이 접해보며 문제 해결에 친숙해져야겠다고 느꼈다.
'Recording > 우아한테크코스 5기 Pre-course' 카테고리의 다른 글
[우테코 5기] <3주차> 'java-lotto' 회고 (0) | 2022.12.14 |
---|---|
[우테코 5기] <2주차> 'java-baseball' 회고 (1) | 2022.12.13 |
[우테코 5기] <1주차> 'java-onboarding' 회고 (2) | 2022.11.29 |
[우테코 5기] <0주차> OT 후기 (0) | 2022.11.06 |