[우아한테크코스 7기] 프리코스 1주차 회고

단디·2024년 10월 30일
0

1주차 과제 깃허브 링크

시작하며


How to start?

우연한 기회에 우아한테크코스 입학설명회를 시청하게 됐어요.

왜 성장하고 싶은가요? 남들을 따라서 하고 있나요?
생각에 대한 생각을 자주하시나요?

평소 가지고 있던 몰입, 메타인지, 집단지성, 명시적 논리적 글쓰기라는 키워드들에 꽂혀있던 저에게 설명회는 큰 파문으로 찾아왔어요.

"그래! 프리코스는 누구나 참여 가능한데 한 번 도전해보자"란 생각으로 아무것도 모르지만 뛰어 들었답니다.

우아한테크코스의 모토가 정말 마음에 들었어요.
특히 뛰어난 사람들과 같이 협업을 할 기회가 생긴다는 것이 너무 탐이 났죠.
시간이 지날수록 기대하고 욕심이 나는 것이 저뿐이 아니겠죠?
그러면 처음해보는 1주차 회고를 시작해 보겠습니다.

1주차를 진행하며

개발 과정 요구 사항

과제 진행, 기능, 프로그래밍 요구 사항 세 가지 모두 충족하기
기능 구현 전 기능 목록을 만들기
기능 단위로 커밋하는 방식으로 진행하기
기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현하기

  • Git
  • IntellJ
  • Java JDK 21
  • README.md 마크 다운 다루기
  • AngularJS Git Commit Message Conventions
  • 자바 코드 컨벤션 (Java Style Guide)

1주차 과제가 진행되면서 필요한 사항들을 준비하게 되었어요.
Git 을 다루는 방법을 배우고 Github 계정을 만들었어요.
가장 도움이 많이 된 사이트를 첨부할게요. [Link]

우테코에서 제공해주는 제출가이드와 각종 안내문(링크)들을 보면서 하나씩 배워갔어요. (도움이 정말 많이 됩니다. 👍)
IntellJ 설치 및 적응, Java JDK 21 로 설정
우선 JDK 가 무엇인 지 23 버전으로 나오는 것을 고치고
git command 를 배운 것을 적용해보고
README 작성을 도대체 어떻게 하는거지?!! 라면서 우선 해보고
커밋을 하고 취소하고 넘어져보고 하면서 어찌저찌 과제를 시작하게 되었어요.
디스코드에서 함께 나누기와 토론, 구글에 많은 정보들이 많은 도움이 된 것 같아요.

특히 바로 시작을 안하고 객체지향의사실과오해 책을 읽으면서 객체지향에 사랑에 빠진 게 된 것 같아요. (강추입니다! 책 추천 받아요 ☺️☺️)

낯설었지만 설렘을 가지고 배우다보니 점점 익숙해진 것 같아요.
아직도 많이 부족하지만 점점 좋아지겠죠?

과제 기능 요구 사항

입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다.

  • 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다.
  • 예: "" => 0, "1,2" => 3, "1,2,3" => 6, "1,2:3" => 6
  • 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다.
  • 예를 들어 "//;\n1;2;3"과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다.
  • 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.

객체지향사실과오해를 읽고 책임, 협력, 역할을 생각하면서 시작을 하려 했어요.
하지만 생각과 구현은 처음에는 갭이 컸어요.
자바라는 언어가 처음이고 너무 완벽하게 하려고 실수를 하면 안된다는 생각이 있었던 것 같아요.
프리코스 과정은 원래라는 건 지워버리고 부딪힘에 망설임이 없어야 한다고 생각해요. (모두 화이팅입니다!🫡)

본론

무엇을 해야할 지 몰라서 과제에 적힌 것을 실천하는 것에 집중을 했어요.

과제 진행, 기능, 프로그래밍 요구 사항 세 가지 모두 충족하기
3가지 부분들을 꼼꼼히 읽어보면서 하나씩 했어요.
그리고 책을 바탕으로 먼저 객체 메세지 흐름을 만들어봤어요.

객체 메세지 흐름
Main -> InputHandler : 문자열 입력 받아줘
InputHandler -> Main : 입력 문자열 받아줘
Main -> Parser : 문자열 파싱해줘
Parser -> Main : 파싱된 문자열 배열 받아줘
Main -> Validator : 양수 검증해줘
Validator -> Main : 검증된 양수 배열 받아줘
Main -> Calculator : 양수 배열 합해줘
Calculator -> Main : 계산 결과 받아줘
Main -> Printer : 계산 결과 출력해줘

이것을 바탕으로 기능 목록도 작성을 하고 프로그래밍에 들어갔습니다.

기능 목록 List
Application(main)
InputHandler
Parser
Validator
Calculator
Printer


1주차에서 작성한 코드

package calculator;

public class Application {
    public static void main(String[] args) {
        InputHandler inputHandler = new InputHandler();
        String userInput = inputHandler.getInput();

        Parser parser = new Parser();
        String[] parsedTokens = parser.parseInput(userInput);

        PositiveNumberValidator validator = new PositiveNumberValidator();
        int[] validatedNumbers = validator.validateNumber(parsedTokens);

        SumCalculator calculator = new SumCalculator();
        int result = calculator.calculateSum(validatedNumbers);

        Printer printer = new Printer();
        printer.printResult(result);
    }
}

MVC 패턴에 대해서 모르는 상태에서 작성을 했어요.
빈 공백을 넣어도 되는 지 클래스 선언, 메소드 사용 등 전부 새로 배워가면서 시작했어요.
객체지향이 아니라 절차지향으로 작성이 된 것 같아요. 🥲

package calculator;

import camp.nextstep.edu.missionutils.Console;

public class InputHandler {
    public String getInput() {
        System.out.println("덧셈할 문자열을 입력해 주세요.");
        return Console.readLine();
    }
}

화면에 처음 출력을 하면서부터 재미를 느끼기 시작했어요.

package calculator;

public class Printer {
    public void printResult(int result) {
        System.out.println("결과 : " + result);
    }
}

최대한 단일 책임을 주려고 설계에 많은 시간을 들였어요.

package calculator;

import java.util.ArrayList;
import java.util.List;

public class Parser {
    //기본 구분자, 참조/내용 변경 불가능
    private final List<String> defaultDelimiters = List.of(",",":");
    //커스텀 구분자, 참조 변경 불가능
    private final List<String> customDelimiters = new ArrayList<>();

    public String[] parseInput(String input) {
        if (input.startsWith("//")){
            input = extractCustomDelimiter(input);
            String regex = String.join("|", defaultDelimiters) + "|" + String.join("|", customDelimiters);
            return input.split(regex);
        } else{
            String regex = String.join("|", defaultDelimiters);
            return input.split(regex);
        }
    }
    private String extractCustomDelimiter(String input) {
        int delimiterEndIndex = input.indexOf("\\n");

        if (delimiterEndIndex == -1)
            throw new IllegalArgumentException("Invalid delimiter: " + input);

        String customDelimiter = input.substring(2, delimiterEndIndex);

        if (!customDelimiters.contains(customDelimiter)){
            customDelimiters.add(customDelimiter);
        }

        return input.substring(delimiterEndIndex+2);
    }
}

private, final, 네이밍이 너무 어려웠어요.
파이썬을 조금 배워둔 것이 도움이 많이 되었어요.

package calculator;

public class PositiveNumberValidator {
    public int[] validateNumber(String[] tokens) {
        // 빈 문자열 or 숫자가 없는 경우 예외 처리
        if (tokens == null || tokens.length == 1 && tokens[0].isEmpty()) {
            return new int[]{0};
        }

        int[] validatedNumbers = new int[tokens.length];

        for (int i = 0; i < tokens.length; i++) {
            try {
                int number = Integer.parseInt(tokens[i]);
                if (number > 0) {
                    validatedNumbers[i] = number;
                } else {
                    throw new IllegalArgumentException("The number must be positive: " + tokens[i]);
                }
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("Invalid number format: " + tokens[i], e);
            }
        }
        return validatedNumbers;
    }
}

여러 메서드를 배우고 예외를 처리하는 경험을 했어요.

package calculator;

public class SumCalculator {
    public int calculateSum(int[] numbers) {
        int sum = 0;
        for (int number : numbers) {
            sum += number;
        }
        return sum;
    }
}
package calculator;

import camp.nextstep.edu.missionutils.test.NsTest;
import org.junit.jupiter.api.Test;

import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class ApplicationTest extends NsTest {
    @Test
    void 커스텀_구분자_사용() {
        assertSimpleTest(() -> {
            run("//;\\n1");
            assertThat(output()).contains("결과 : 1");
        });
    }

    @Test
    void 예외_테스트() {
        assertSimpleTest(() ->
            assertThatThrownBy(() -> runException("-1,2,3"))
                .isInstanceOf(IllegalArgumentException.class)
        );
    }

    @Override
    public void runMain() {
        Application.main(new String[]{});
    }
}

./gradlew clean test 를 돌리면서 수정을 할 수 있었어요.
이때 테스트에 대해서 중요성을 느끼게 됐어요.

예외 처리

  • 입력된 문자열에서 숫자를 추출하여 더하는 계산기가 요구 사항이기때문에 숫자가 없거나 빈 문자열일 경우 0으로 가정한다.
  • 사용자가 잘못된 값을 입력한 경우 IllegaArgumentException을 발생시킨 후 애플리케이션 종료
  • 구분자는 잘못된 입력에서 제외로 본다.
  • 음수의 경우
  • 커스텀 구분자의 /// or \n\n 경우
  • 숫자가 없는 경우, 커스텀구분자를 //x\n 이 있을 때 x 가 여러 개의 특수문자일 경우 하나로 볼지 여러 개로 볼지 결정.

당시 처음 예외 사항에 대해서 생각해봐서 미흡했어요.
아직도 가장 어려운 부분이 이 예외 사항인 것 같아요.
요구 사항과 예외 사항을 만족하느 것이 중요한 것 같아요.


1주차 끝나자마자 기록했던 부분

  • README 작성에 대해서 공부가 필요하고 Markdown 형식에 익숙해져야한다는 것을 느꼈습니다.
  • Git, Github 을 이용한 개발을 진행하면서 Mac, Window 다양한 플랫폼과 기기에서 편리하게 할 수 있는 것을 느꼈고 협업시에는 적극 활용해야겠다고 느꼈습니다.
  • 새로운 IDE 및 언어에 겁을 먹지말고 적극적으로 다가감으로써 빠르게 적응할 수 있어야한다고 생각하게 되었습니다.
  • 기능 목록 하나씩 구현을 하면서 테스트와 커밋에 대해서 미흡함을 느꼈습니다, 더욱 복잡한 개발을 할 때에 이런식으로 하면 어려움이 생길 것이라고 느꼈습니다.
  • 객체지향의 사실과 오해를 읽으면서 객체의 메세지와 책임을 생각하며 기능 목록을 만들어보고 싶어 시간을 많이 투자를 했습니다, 다만 추상적으로 실제로 만드는 부분에서 많이 경험을 해봐야겠다고 느꼈습니다.
  • 생각한 것을 구현하는데에 있어 기본 형식과 Method에 대한 공부가 부족하여 시간이 많이 지체되었습니다.
  • 출력: 출력 : 정확한 요구사항 검토가 필요한 것을 느꼈습니다.
    Validator, Calculator, Printer, ErrorHandler 를 기능 목록에 넣어두고 실제 구현에서는 급하고 낯설단 이유로 그대로 진행을 안했습니다, 계획대로 리팩토링하면서 본래 의도대로 진행하고 다음 과제부턴 좀 더 신경을 쓰도록 해야겠습니다.
  • README 수정, git commit 등 제출해야한다는 생각에 실패를 피할려고 하는 모습이 있었고 그로인해 시간이 많이 소요되었습니다, 지금은 배우는 단계이므로 조금 더 거침없이 부딪혀가는 모습을 가지고 프리코스에 임해야겠다고 느꼈습니다.
  • Markdown 들여쓰기가 원하는대로 나오질 않아 커밋을 여러 번 진행하게 되었고 그것들을 합치기 위해서 rebase, squash, push --force 를 처음 사용해봤습니다, 개인 과제라서 괜찮은 행동인 것 같지만 여러 방면으로 무엇을 조심하고 어떻게 하면 좋은지에 대해서 검색을 통해서 공부를 해야겠다고 이번 기회에 느꼈습니다. 이번 프리코스에서 느낀점같은 회고는 README 말고 다른 곳에 해야하는지에 대해선 1주차 프리코스가 끝나고 다른 동료들을 보면서 깨달아야겠습니다.
  • ErrorHandler 클래스를 만들다가 전체적으로 꼬임이 발생해서 에러핸들러는 기능 목록에서 지웠다. 현재 지식이 부족하여 발생한 것으로 생각된다. 자바 책과 여러 지식을 쌓으면서 다시 회고를 통해 돌아봐야겠다. 또한 생각해보니 메인을 통한 중앙집중식으로 프로그램을 만든 것 같다는 생각이 들었다. 그래서 인풋핸들러가 파서에게 파서가 검증기, 검증기가 계산기 등 객체의 협력 구조로 바꿀려고 생각을 했다. 하지만 의존성에 대해서 아직 지식이 미비하여 무엇이 더 의존성이 낮고 여러모로 좋은지에 대해서 정확하게 모르겠어서 지금의 로직을 지키고 이것또한 회고를 통해서 꼭 다시 돌이켜 볼 예정이다.
  • 클래스 인스턴스를 메인 밑에 함께 형성할 지 로직의 순서대로 형성할 지에 대해서도 고민을 하다가 위와 같은 이유로 유지를 하였다. 한편으로는 프로그램이 커지고 DI를 하게된다면 모으는 것이 보기에 좋겠단 생각을 하였다.

마무리하며

1차 공통 피드백

1주 차 미션의 학습 목표는 개발 환경과 프로그래밍 언어에 익숙해지는 것이었습니다.

  • 요구 사항을 정확하게 준수한다
  • 기본적인 Git 명령어를 숙지한다
  • Git으로 관리할 자원을 고려한다
  • 커밋 메시지를 의미 있게 작성한다
  • 오류를 찾을 때 출력 함수 대신 디버거를 사용한다
  • 이름을 통해 의도를 드러낸다
  • 축약하지 않는다
  • 공백도 코딩 컨벤션이다
  • 스페이스와 탭을 혼용하지 않는다
  • 의미 없는 주석을 달지 않는다
  • 코드 포매팅을 사용한다
  • Java에서 제공하는 API를 적극 활용한다
  • 배열 대신 컬렉션을 사용한다

개발 환경과 프로그래밍 언어에 익숙해지는 것이 학습 목표였어요.
요구 사항을 따르기 위해서 노력을 하다보면 자연스럽게 목표를 이룰 수 있게 과정이 잘 짜여진 것 같아요.
1차 공통 피드백에 있는 내용들이 지금 봐도 매우 중요한 부분들이라고 느껴지네요.

일주일 동안에 많은 것을 할 수 있었고 가장 큰 것은 자기 주도적으로 학습을 얼마든지 할 수 있다는 용기 인 것 같아요.

결과를 떠나서 프리코스 적극 추천합니다.

profile
협업, 문제해결, 지속적 학습을 추구하는 개발자 지망생 단디입니다.

0개의 댓글