1주차 피드백

[[우테코 7기 프리코스] 1주차 – 문자열 덧셈 계산기](https://velog.io/@grayson1999/우테코-7기-프리코스-1주차-문자열-덧셈-계산기)

한 주의 시작인 화요일에 피드백 메일과 새로운 미션이 도착했다. 프리코스를 시작한 뒤로 요즘 하루하루가 빨리 지나가는 느낌이다. 예전 학교에서 배웠던 내용을 복습하는 내용도 있지만, 그것을 새롭게 정리하면서 최근에 자신감이 많이 붙었다. 다크서클은 덤이지만, 최근에 학교 시험까지 겹쳐서 정신없이 바쁘다. 입학 설명회에서 말씀하신 것처럼 졸업만 하면 되니까, 피드백을 먼저 시작해 보려고 한다.


1주차 피드백

1주 차 미션의 학습 목표는 개발 환경과 프로그래밍 언어에 익숙해지는 것이다. 이번 프로젝트를 통해 간단한 IntelliJ에서의 환경 구축과 Git 사용법을 배웠다. 또한 AngularJS 커밋 규칙을 통해 협업 간 커밋 메시지 작성 방법을 공부했다.

피드백 강의 영상

기능 목록 작성

이번 프로젝트를 진행하면서 가장 어려웠던 부분 중 하나는 기능 목록 작성이었다. 미리 주어진 요구사항에서 얼마나 깊게 기능 목록을 작성해야 할지 고민이 많았다. 요구 분석을 어떻게 분류해야 할지도 의문이었다. 예를 들어, v0.1부터 v1.0까지 각각의 단계를 버전별로 나눠서 기능 목록을 작성해야 하는지, 아니면 각 기능을 개별적으로 분리해서 작성해야 하는지 궁금했다. 또한 개발 순서를 어떻게 정해야 하는지, 순서도를 작성해야 하는지 여러 가지 고민이 있었다.

이번 강의를 통해 해결한 부분은 각 요구 사항을 너무 깊게 작성할 필요는 없다는 것이다. 깊이의 기준은 내가 요구 사항을 한 번 분석한 뒤 다시 한 번 더 깊게 작성했던 경험에서 판단했다. 강의를 참고한 후 첫 번째 요구 사항을 분석한 뒤 개발을 시작하는 것을 보며 요구 사항 분석의 깊이를 가늠할 수 있었다. 또한 버전별로 굳이 나눌 필요가 없다는 생각이 들었다.

물론 여기에서는 코딩 테스트라는 제한 사항이 존재한다. 따라서 각각의 프로젝트별 요구 사항 분석은 다를 것이다. 예를 들어, 큰 프로젝트에서는 객체 모델링을 시작으로 UML을 작성하는 것이 일반적이다. 그러나 이번 프로젝트는 그리 깊은 요구 사항 분석을 요구하지 않는다는 것을 알게 되었다.

1. 문자열 파싱

1.1 쉼표 또는 콜론 구분자로 분리
- 입력 문자열에서 쉼표(,)와 콜론(:)을 기준으로 숫자를 추출한다.
- 구분자가 혼합된 경우에도 이를 처리할 수 있어야 한다.
- 공백이 입력되더라도 정상 작동해야 한다.
- 예시: "1,2,3" → {1, 2, 3}, "1,2:3" → {1, 2, 3}, "" → {}

1.2 커스텀 구분자 처리
- 문자열이 "//"와 "\n"으로 시작하면, 그 사이의 문자를 커스텀 구분자로 인식한다.
- 예시: "//;\n1;2;3" → 커스텀 구분자 ;를 사용하여 [1, 2, 3]을 추출
v0.2 - 쉼표 또는 콜론 구분자 분리 기능 구현
- 입력 문자열에서 쉼표(,)와 콜론(:)을 기준으로 숫자를 분리하는 기능을 구현한다.
	* 사용자로부터 입력 문자열을 받는다.
	* 빈 문자열을 입력 받으면 빈 배열로 반환한다.
	* 입력 문자열을 쉼표(,) 또는 콜론(:)으로 분리한다.
	* 공백이 포함된 경우에도 숫자만 추출한다.
	* 추출한 숫자를 리스트나 배열로 반환한다.
	
v0.3 - 커스텀 구분자 분리 기능 구현
- 커스텀 구분자를 처리하는 기능을 구현한다.
	* 입력 문자열이 "//"와 "\n"으로 시작하는지 확인한다.
	* 커스텀 구분자를 추출하고 이를 사용하여 숫자를 분리한다.
	* 커스텀 구분자가 여러 개인 경우에도 처리할 수 있도록 구현한다.

테스트 코드

다음은 테스트 코드를 언제 작성해야 하는가에 대한 부분이다. 나는 테스트 코드를 가장 마지막에 한 번에 작성했지만, 강의를 통해 각각의 기능을 구현하면서 작성하는 것이 옳다는 것을 알게 되었다. 사실 테스트 코드는 기능을 구현하면서 작성하는 것이 맞다는 것은 알고 있다. 하지만 나는 System.out.println을 통해 테스트를 해왔기 때문에 이 점을 잊고 있었다.

예외처리

예외 처리 부분은 중요한 요소였다. 이번 개발에서는 먼저 예외 처리 사항을 정리한 후 개발에 들어갔다. 그 후에도 여러 예외 처리할 부분이 생겨서 README에 각각 기록하며 진행했던 기억이 있다. 예외 처리에 구렁에 빠져 이리저리 정신이 없던 기억도 난다.

해당 피드백 강의에서는 예상하지 못한 예외나 요구 사항에 드러나지 않은 예외 처리는 주석을 통해 메모한 뒤 다음으로 미루라고 제안했다. 그 이유는 큰 요소를 놓칠 가능성이 크기 때문이다. 따라서 먼저 기능을 작성한 후 예외 처리를 하는 것도 늦지 않다.

개발 순서

순서에 대한 생각은 강의에서 나오지 않았지만, 우리는 객체 지향 언어를 사용한다. 따라서 어떤 순서로 구현하든 각 기능을 test 함수를 통해 테스트하고, 그 후 리팩토링을 통해 좋은 코드를 작성하는 것이 바람직하다고 생각했다.


요구 사항을 정확하게 준수한다.

과제를 제출하기 전에 과제 진행 요구 사항, 기능 요구 사항, 프로그래밍 요구 사항을 모두 충족하였는지 다시 한 번 확인한다. 이러한 요구 사항은 미션마다 다르므로 항상 주의 깊게 읽어야 한다.

Git

기본적인 Git 명령어를 숙지한다.

이번 프로젝트를 통해 Git이 동작하는 과정을 이해하고 기본적인 명령어를 숙지하였다. 또한 1주차 공통 피드백에 있는 rebase, cherry-pick, switch 등의 명령어도 추가적으로 공부하였다.

[[Git] Git의 기본 구조 이해하기](https://velog.io/@grayson1999/Git%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0)

[[Git] Git 기본 명령어 정리](https://velog.io/@grayson1999/Git-Git-기본-명령어-정리)

Git으로 관리할 자원을 고려한다

이번 피드백에서 한번 더 고민했던 부분이 관리할 자원을 고려한다는 부분이었다.

흔히 우리가 사용하는 .gitignore 파일에서 환경 변수 파일 또는 비밀키 등 보안 관련된 코드나, AI model과 같이 크기가 큰 파일을 작성하여 추적을 제한했던 경험이 있다.

하지만, java 코드가 있다면 .class 파일을 생성할 수 있고 IntelliJ IDEA의 .idea 폴더, Eclipse의 .metadata폴더 모두 자동 생성되는 파일이므로 Git으로 관리가 필요가 없다는 것이다.

[[Git] .gitignore 사용법](https://velog.io/@grayson1999/Git-.gitignore-%EC%82%AC%EC%9A%A9%EB%B2%95)

커밋 메시지를 의미 있게 작성한다

커밋 메시지는 협업에서 중요한 역할을 한다는 것을 배웠다. 좋은 커밋 메시지는 효율적인 협업을 도와준다는 것을 깨달았고, 몇 가지 규칙이 있다는 것을 확인하였다. 주로 커밋의 제목에 type을 명시하여 해당 커밋을 구분하고, 코드의 수정이나 해당 커밋의 내용을 잘 이해할 수 있도록 한다.

[[Git] AngularJS Git 커밋 메시지 규칙 정리](https://velog.io/@grayson1999/AngularJS-Git-커밋-메시지-규칙-정리)

커밋 메시지에 이슈 또는 풀 리퀘스트 번호를 포함하지 않는다

일부 프로젝트에서는 커밋 메시지에 이슈 또는 풀 리퀘스트 번호를 포함하지만, 이는 원본 저장소의 관련 없는 이슈나 풀 리퀘스트에 영향을 줄 수 있으므로 피해야 한다.

풀 리퀘스트를 만든 후에는 닫지 말고 추가 커밋을 한다

이미 생성된 풀 리퀘스트에는 추가 커밋을 통해 변경 사항을 반영할 수 있다. 새로운 풀 리퀘스트를 새로 만들 필요가 없다는 것이다.

Coding

오류를 찾을 때 출력 함수 대신 디버거를 사용한다

system.out.print()를 사용하여 매번 코드를 실행하여 문제를 파악할 수 있으나, 이는 불효율적이며, 불필요한 코드가 남을 수 있다. 하지만 디버거를 이용하면 코드 내부의 상태 값이 어떻게 변하는지 흐름을 파악할 수 있다는 장점이 있다.

사실 디버거 대신 주로 sout을 사용해 왔다. 왜 디버거를 쓰지 않았는지 생각해 보니, 디버거는 내가 원하는 변수 외에도 너무 많은 정보를 제공해 복잡하게 느껴졌기 때문이었다. 이번 피드백을 통해 디버거 사용법을 배우면서, 이제 더 효율적으로 디버깅할 수 있게 되었다.

[[codingPractices] IntelliJ에서 디버깅 사용법](https://velog.io/@grayson1999/IntelliJ%EC%97%90%EC%84%9C-%EB%94%94%EB%B2%84%EA%B9%85-%EC%82%AC%EC%9A%A9%EB%B2%95)

이름을 통해 의도를 드러낸다

좋은 이름을 짓는 것은 소통을 위해 중요한 활동 중 하나이다. 변수 이름, 함수 이름, 클래스 이름을 짓는데 여러 방법이 존재한다. 이를 통해 유지보수 용이, 디버깅 시간 절약, 문서화 필요성 감소 등 여러 이점을 가질 수 있다.

축약하지 않는다

이름을 작명할 때 이름을 줄이려는 유혹에 빠지곤 한다. 하지만 의도를 드러낼 수 있다면 이름이 길어져도 괜찮다.

평소 변수 이름에 크게 신경 쓰지 않다 보면 무심코 넘어가는 일이 많다. 이번 피드백을 통해 작명법에 대해 다시 한번 정리하면서, 함수에는 일반 동사를, 상태 변수에는 be동사를 사용하는 것이 좋다는 등 여러 작명 규칙을 공부하고 이를 더 신경 쓰게 되었다.

[[codingPractices] 변수와 함수 작명법](https://velog.io/@grayson1999/%EB%B3%80%EC%88%98%EC%99%80-%ED%95%A8%EC%88%98-%EC%9E%91%EB%AA%85%EB%B2%95)

공백도 코딩 컨벤션이다

즉, 공백도 중요한 규칙이라는 뜻으로 적절하게 사용해야 한다.

코드의 가독성을 높이기 위해 if, for, while 문 사이에 적절한 공백을 두는 것이 중요하다. 이렇게 하면 각 블록이 시각적으로 분리되어 다른 개발자가 코드를 더 쉽게 이해할 수 있다.

공백 라인을 의미 있게 사용한다

공백 라인은 문맥을 분리하는 데 유용하다.

Java 스타일 가이드에 따르면 “수직 공백은 가독성을 개선하는 곳이면 어디든 나타날 수 있다”고 말한다. 각 기능이나 논리 블록 사이에 적절하게 공백을 두어 코드의 흐름을 명확히 해야 한다. 하지만 과도한 공백은 혼란을 줄 수 있으므로 적절한 사용이 필요하다.

스페이스와 탭을 혼용하지 않는다

들여쓰기 시 스페이스와 탭을 혼용하면 코드의 일관성이 떨어진다.
따라서 둘 중 하나만 선택해 사용하는 것이 좋다.

의미 없는 주석을 달지 않는다

변수나 함수 이름이 명확한 의도를 드러낸다면 주석은 불필요하다.

가능하면 이름으로 의도를 나타내고, 주석이 필요할 경우에는 간결하고 구체적으로 작성해야 한다.

/*
     * 기본 구분자(쉼표와 콜론)를 사용하여 주어진 문자열을 나누고 int 배열로 변환하여 반환합니다.
     *
     * @param numberLine 숫자와 구분자로 이루어진 문자열
     * @return 구분된 int 숫자 배열
     */
    public static int[] splitDelimiters(String numberLine) {
        return splitDelimiters(numberLine, "[,:]");
    }

    /*
     * 주어진 int 배열의 모든 숫자를 더하여 합계를 반환합니다.
     *
     * @param numbers 합산할 숫자들이 담긴 int 배열
     * @return 숫자들의 합계
     */
    public static int arrSum(int[] numbers){
        int sum = 0;

        //빈 배열을 입력 되었을 경우 처리
        if(numbers.length == 0){
            return 0;
        }

        for(int number:numbers){
            // 양수가 아닌 경우 예외 발생
            if (number <= 0){
                throw new IllegalArgumentException("Negative numbers or zero are not allowed : " + number);
            }
            sum += number;
        }

        return sum;
    }

이번 프로젝트에서 작성한 주석 내용 중 일부분이다. 함수 이름에서 의도가 명확하게 드러나는지 고민해 볼 필요가 있다. 이러한 경우 인터페이스를 구현한 후 각각의 이름을 작성하는 것이 함수 이름을 더 명확하게 만들 수 있지 않았을까 하는 생각이 든다.

코드 포매팅을 사용한다

IDE의 자동 정렬 기능을 사용하면 코드가 깔끔해지고 가독성이 높아진다. 각 IDE에서 제공하는 포맷 단축키를 활용하여 일관된 스타일을 유지하자.

  • IntelliJ IDEA: ⌥⌘L, Ctrl+Alt+L
  • Eclipse: ⇧⌘F, Ctrl+Shift+F
  • Visual Studio Code: ⇧⌥F, Shift+Alt+F

Java에서 제공하는 API를 적극 활용한다

함수를 직접 구현하기 전에 Java API에서 해당 함수를 제공하는지 확인하는 것이 좋다.

예를 들어, 사용자를 출력할 때 두 명 이상의 사용자가 있을 경우, 쉼표(,)로 구분된 문자열로 출력할 수 있다. 아래는 그 예시이다.

var members = List.of("pobi", "jason");
var result = String.join(",", members); // pobi,jason

배열 대신 컬렉션을 사용한다

Java의 컬렉션 프레임워크(List, Set, Map 등)를 사용하면 다양한 API를 통해 데이터를 더 쉽게 조작할 수 있다.

예를 들어, List에 특정 값이 포함되어 있는지 확인하려면 다음과 같이 할 수 있다.

var members = List.of("pobi", "jason");
var result = members.contains("pobi"); // true

그럼 나는 어떻게 구현했을까?

...
System.out.printf("결과: %d", arrSum(arrayUserInput));
...
public static int arrSum(int[] numbers) {
    int sum = 0;

    // 빈 배열이 입력된 경우 처리
    if (numbers.length == 0) {
        return 0;
    }

    for (int number : numbers) {
        // 양수가 아닌 경우 예외 발생
        if (number <= 0) {
            throw new IllegalArgumentException("Negative numbers or zero are not allowed: " + number);
        }
        sum += number;
    }

    return sum;
}

아무 기능 없이 배열을 더하는 함수를 구현했다.

컬렉션을 사용하지 않았고 Java API도 활용하지 않았다. 가독성을 높이려고 했던 것도 아니다.

이처럼 간단한 함수의 경우, 구현하려는 습관이 남아있어서 그랬던 것 같다. 이번 피드백을 통해 이 점을 개선해봐야겠다.

위로 스크롤