[우테코 프리코스] 1주차 회고

예름·2024년 10월 23일
2

우테코

목록 보기
2/2
post-thumbnail

0. 시작 전

학습 목표

  • Git, GitHub, IDE 등 실제 개발을 위한 환경에 익숙해진다.
  • 교육 분야에 맞는 프로그래밍 언어를 사용하여 간단한 문제를 해결한다.

프리코스 진행 방식

진행 방식

  • 미션은 과제 진행 요구 사항, 기능 요구 사항, 프로그래밍 요구 사항 세 가지로 구성되어 있다.
  • 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다.
  • 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.
  • 매주 진행할 미션은 화요일 오후 3시부터 확인할 수 있으며, 다음 주 월요일까지 구현을 완료하여 제출해야 한다. 제출은 일요일 오후 3시부터 가능하다.
    • 정해진 시간을 지키지 않을 경우 미션을 제출하지 않은 것으로 간주한다.
    • 종료 일시 이후에는 추가 푸시를 허용하지 않는다.

미션 제출 방법

  • 미션 구현을 완료한 후 GitHub을 통해 제출해야 한다.
  • GitHub에 미션을 제출한 후 우아한테크코스 지원 플랫폼에 PR 링크를 포함하여 최종 제출한다.
    • 자세한 안내는 제출 가이드를 참고한다.
      - 과제를 수행하면서 느낀 점, 배운 점, 많은 시간을 투자한 부분 등 자유롭게 작성한다.

과제 제출 전 체크 리스트

  • 기능을 올바르게 구현했더라도 요구 사항에 명시된 출력 형식을 따르지 않으면 0점을 받게 된다.
  • 기능 구현을 완료한 후 아래 가이드에 따라 모든 테스트가 성공적으로 실행되는지 확인한다.
  • 테스트가 실패하면 점수가 0점이 되므로 제출하기 전에 반드시 확인한다.

테스트 실행 가이드

  • 터미널에서 java -version을 실행하여 Java 버전이 21인지 확인한다. Eclipse 또는 IntelliJ IDEA와 같은 IDE에서 Java 21로 실행되는지 확인한다.
  • 터미널에서 Mac 또는 Linux 사용자의 경우 ./gradlew clean test 명령을 실행하고, Windows 사용자의 경우 gradlew.bat clean test 또는 ./gradlew.bat clean test 명령을 실행할 때 모든 테스트가 아래와 같이 통과하는지 확인한다.
BUILD SUCCESSFUL in 0s

문자열 덧셈 계산기

과제 진행 요구 사항

  • 미션은 문자열 덧셈 계산기 저장소를 포크하고 클론하는 것으로 시작한다.
  • 기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다.
  • Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다.
  • 자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다.

기능 요구 사항

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

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

입출력 요구 사항

입력
  • 구분자와 양수로 구성된 문자열
출력
  • 덧셈 결과
결과 : 6
실행 결과 예시
덧셈할 문자열을 입력해 주세요.
1,2:3
결과 : 6

프로그래밍 요구 사항

  • JDK 21 버전에서 실행 가능해야 한다.
  • 프로그램 실행의 시작점은 Applicationmain()이다.
  • build.gradle 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.
  • 프로그램 종료 시 System.exit()를 호출하지 않는다.
  • 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다.
  • 자바 코드 컨벤션을 지키면서 프로그래밍한다.

라이브러리

  • camp.nextstep.edu.missionutils에서 제공하는 Console API를 사용하여 구현해야 한다.
    • 사용자가 입력하는 값은 camp.nextstep.edu.missionutils.ConsolereadLine()을 활용한다.

1. 프로젝트 fork/clone 하기

프로젝트를 자신의 계정으로 fork하기

woowacourse 저장소 우측 상단의 fork 버튼을 클릭해 fork합니다

fork한 저장소를 자신의 컴퓨터로 clone하기

git clone https://github.com/{본인_아이디}/java-calculator-7.git

기능 구현을 위한 브랜치 생성

git checkout -b {본인 아이디}

통합 개발 환경(IDE)으로 가져오기

IntelliJ 시작 후 프로젝트 import


2. 기능 구현

PR 링크


3. 고민/이슈/배운점

고민

  1. README.md에 구현할 기능 목록 정리 어떻게 할지 → Angular Git Commit Message Conventions가 도움이 될까?
  2. 잘못된 값(예외처리) 어떻게, 어디까지 할지
  3. 입력은 직접 입력? → 프로그래밍 요구사항 라이브러리 참고
  4. 테스트 코드는 어떻게? 구현 전?후? → 일단 TDD를 공부하자
  5. 일단 코드는 굴러가는데 더 나은 코드를 짜려면 어떻게 해야할까
  6. 다른 예외가 더 있을까

이슈

  1. 테스트코드를 생각하다가 생각의 굴레에 빠졌다.
  2. 생각보다 기능 구현에서 애를 먹었다. 다행히 굴러가게끔 완성은 했는데 리팩토링이 필요하고 README.md에 작성한 것과 달려졌다.

배운점

  1. Coding Conventions는 코딩 규칙이다.
  2. 테스트코드를 먼저 짜고 구현을 하니 어떤 부분에서 문제가 생겼는지 더 명확히 알 수 있었다. TDD에 대해 더 공부를 해야겠지만 TDD를 하는 이유가 조금은 이해가 됐다.
  3. 기능 단위로 커밋을 하니 확실히 커밋 로그를 볼 때 어느 파일을 무엇을 수정했는지 확인할 수 있어서 알아보고 이해하기 좋았다.

4. 스터디

피드백

  • 변수명은 축약어를 사용하면 무엇을 의미하는지 모를수도 있다, 확실한 뜻을 보이도록 네이밍!
  • 출력하려는 문구는 상수로 빼서 관리 -> 빠른 유지 보수를 위해
  • 클래스의 역할 분배
  • 메서드 분리 -> 책임 분리와 네이밍에 있어서 더 나은 구현
  • 의미가 있는 이름을 사용해서 주석 없이도 이해가 될 수 있게 작성(주석의 내용을 메서드에 담기)
  • 에러 객체를 반환할 때 메세지 추가 -> 어떤 오류인지 확인하기 수월

스터디 토의 주제

MVC 패턴에서의 View는 static으로 구현해도 될까?

  • 정답은 없다 취향 차이일듯
  • MVC 패턴을 엄밀하게 따지려면 controller와 view의 상호작용을 확실하게 하기 위해서

Model에 멤버 변수를 넣어야 할까?

  • MVC패턴이라고 해서 꼭 모델의 성질을 이용하지 않아도 된다!

Object... params 문법 뭔가요?

  • 몇개가 들어올지 모르지만 받아내겟다
public PrintWriter printf(String format, Object... args)
System.out.printf("출력 format", 출력할 내용);

커밋단위 = 기능단위여야할까?

  • 계산하기
    • 숫자받기
    • 덧셈하기
    • 계산출력하기

→ 하나의 커밋단위 !

feat(View): hi!;

테스트 방법

  • 단위 테스트/통합 테스트

https://junit.org/junit5/docs/current/user-guide/
https://assertj.github.io/doc/
https://www.baeldung.com/assertj-exception-assertion
https://www.baeldung.com/parameterized-tests-junit-5

Record

public class UserInput{
		// 불변 객체의 특성이므로 final
		private final String input;
		private final boolean hasCustomDelimiter;
		private final List<String> delimiters;
		
		// 1. constructor
		public UserInput(String input, boolean hasCustomDelimiter, List<String> delimiters){
				this.input = input;
				this.hasCustomDelimiter = hasCustomDelimiter;
				this.delimiters = delimiters;
		}
		
		// 2. getter
		public String input(){
				return input;
		}
		public boolean hasCustomDelimiter(){
				return hasCustomDelimiter;
		}
		public List<String> delimiters(){
				return delimiters;
		}
		
		// 3. hashCode
		@Override
		public int hashCode(){
				return Objects.hash(input, hasCustomDelimiter, delimiters);
		}
		
		// 4. equals
		@Override
		public boolean equals(Object o){
				if(this == o) return true;
				if(o == null || getClass() != o.getClass()) return false;
				
				UserInput userInput = (UserInput)o;
				return input.equals(userInput.input())
							 && hasCustomDelimiter == userInput.hasCustomDelimiter()
							 && delimiters.equals(userInput.delimiters());
		}
		
		// 5. toString
		@Override
		public String toString(){
				return "UserInput[input=" + input
					+ ", hasCustomDelimiter=" + hasCustomDelimiter
					+ ", delimiters=" + delimiters + "]";
		} 
		
}
  • record를 사용하면 다음과 같이 표현할 수 있다
public record UserInput(String input,
												boolean hasCustomDelimiter,
												List<String> delimiters){
}
  • body에 추가로 메서드를 작성할 수 있다
public record UserInput(String input,
												boolean hasCustomDelimiter,
												List<String> delimiters){

		public String printFields(){
				return "input은 "+input+"이고, 추출된 구분자들은 "+delimiters+"입니다.";
		}
}

https://mysterlee.tistory.com/102
https://yozm.wishket.com/magazine/detail/2814/

Junit의 @DisplayName

-> 편한데 귀찮음

Split vs StringTokenizer

  • 디미터의 법칙 지키기
a.getB().getC().getD() 
a.getD(); 
-> B.getC().getD();

Singleton

  • util을 싱글톤으로!
  • 인스턴스를 생성하지 않아도 됨
  • 변경을 안해도 되는 것들

5. 회고

이번 과제를 통해 많은 것을 배우며 새로운 환경에 적응하는 시간이 필요했다. 특히 깃허브 사용법과 커밋 메시지 컨벤션에 익숙해지기까지 시간이 걸렸지만 그 과정에서 유익한 점을 많이 배웠다. 과제 요구사항 중 하나였던 커밋 메시지 컨벤션에 대해 처음 알게 되었고 그동안 프로젝트를 하며 나름의 규칙으로 커밋 메시지를 작성해왔던 방식이 조금 부족하다는 생각을 하게 되었다. 커밋 메시지 컨벤션을 정독하며 그 고민이 해소되었고 만약 내가 지금까지의 습관대로 회사에 입사했다면 적응하는 데 어려움이 있었을 것 같다고 느꼈다. 또한 기능 단위로 커밋을 하니 커밋 로그를 확인할 때 수정된 파일과 변경 사항을 명확히 분리해서 볼 수 있어 이해하는 데 많은 도움이 되었다. 이번 과제에서는 커밋 메시지를 영어로 작성했지만 다음 과제부터는 subject line을 제외한 나머지 부분은 한글로 작성하는 것이 더 좋을 것 같다.

과제를 진행하면서 과제 설명이 다소 모호하고 예시가 부족해 본격적으로 구현하기 전에 많은 고민을 하게 되었다. 특히 잘못된 값에 대한 예외 처리를 어떻게 해야 할지, 그리고 테스트 코드를 어떻게 작성해야 할지 고민하는 시간이 길었다. 이번 프리코스에서 TDD를 사용해보기로 마음먹었기 때문에 가능한 모든 예외 상황을 고려하면서 생각이 깊어졌고 이로 인해 생각보다 시간이 많이 소요되었다. 과제 제출이 끝난 후 TDD를 사용해보니 초반에 시간을 많이 들여야 했지만, 구현 과정에서 어느 부분에서 오류가 발생하는지 명확하게 파악할 수 있어 매우 유익했다. 특히 규모가 큰 프로젝트에서 TDD가 유용하게 사용될 것 같아 앞으로도 계속 사용할 계획이다.

과제에서 “문자열 앞부분의 ’//’와 ‘\n’ 사이에 위치하는 문자를 커스텀 구분자로 사용한다”는 설명이 다소 모호하게 느껴졌다. 특히 ‘//’와 ‘\n’ 사이에 여러 문자가 들어갈 경우, 이를 잘못된 입력으로 처리해야 할지, 아니면 각각의 문자를 커스텀 구분자로 봐야 할지 고민이 많았다. 구분자는 한 글자의 문자이지만, 과제 설명에 ‘//’와 ‘\n’ 사이에 하나의 커스텀 구분자만 들어간다는 명확한 언급이 없었기 때문에 여러 문자가 들어갈 경우 각 문자를 개별적인 커스텀 구분자로 해석해야 한다고 판단했다. 따라서 여러 문자가 주어졌을 때 각각의 문자를 커스텀 구분자로 인식하도록 코드를 구현했다.

또 이번 과제의 규모가 크지 않아 Application 클래스를 제외하고 두 개의 클래스만 사용했지만, 엄밀히 말하면 절차지향적으로 코드를 작성했기 때문에 이 방식이 적절한지에 대한 의문이 들었다. README에 기능을 정리하면서도 함수나 코드의 양이 적어서 과연 이렇게 작성하는 것이 맞는지 고민이 되었다. 스터디원들과 코드 리뷰를 통해 피드백을 받고 다른 사람들이 어떻게 구현했는지, 왜 그렇게 구현했는지에 대해 이야기를 나누고 싶다.


6. 기타


이 얘기를 보고 굉장히 위안이 됐다...

profile
안정적인 쳇바퀴를 돌리는 삶

0개의 댓글