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

Park Junha·2024년 10월 21일
post-thumbnail

프리코스에서 설정한 목표는 세 가지였다.
1. 요구사항을 충분히 이해하고 이를 만족시키는 것
2. 확장성과 유지보수성을 고려한 코드 작성
3. 객체 지향 프로그래밍 원칙을 체계적으로 적용해 객체가 자신의 책임을 명확히 하는 것

이 목표들을 충분히 고려하며 코드를 작성했는지 돌아보자.


1. 요구 사항을 충분히 이해하고 이를 만족 시켰는가?

프리코스 미션의 요구 사항으로는 과제 진행 요구 사항, 기능 요구 사항, 프로그래밍 요구 사항 세 가지로 구성되어 있다.

그중에서도 특히 다음 문장이 눈에 띄었다.

기능 요구 사항에서 기재되지 않은 내용은 스스로 판단하여 구현한다.

과제를 단순히 따라가는 것이 아니라, 내가 무엇을 구현해야 할지 스스로 결정할 수 있다는 것을 의미했다.


1-1. 과제 진행 요구 사항

1-2. 기능 요구 사항

[기능 요구 사항]

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

위의 요구 사항을 확인하면서, 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다. 라는 문장의 의미를 더욱 명확히 이해할 수 있었다.

예를 들어:

  1. 커스텀 구분자를 여러 개 만들 수 있는지에 대한 명확한 설명은 없었다.

    • 예시: //$\n//@\n1$2@3과 같은 문자열에서 두 개의 구분자를 사용할 수 있는지에 대한 판단은 직접 내려야 했다.
  2. 커스텀 구분자 생성 시 예외 처리도 스스로 결정해야 했다.

    • //\n 사이에 구분자가 없는 경우:
      • 예시: //\n234
        이 경우 구분자가 없기 때문에 IllegalArgumentException을 발생시켰다. 빈 문자는 구분자로서의 역할을 할 수 없기 때문이다.
    • //\n 사이에 숫자가 있는 경우:
      • 예시: //2\n22324
        숫자를 구분자로 지정하면 그 숫자는 이후 계산에서 사용될 수 없기 때문에, 이 경우에도 IllegalArgumentException을 발생시켰다. 숫자를 구분자로 허용하는 것은 합리적이지 않다고 판단했기 때문이다.

이와 같은 상황들을 고려하면서, 문제를 해결하기 위한 나만의 기준을 세워가며 구현했다.

1-3. 프로그래밍 요구 사항

  • JDK 21버전camp.nextstep.edu.missionutils.ConsolereadLine()을 활용하는 등 요구 사항을 충실히 만족시켰다. 특히, 자바 코드 컨벤션에 대해 xml 설정 방법을 디스코드의 함께-나누기 채널에 공유하여, 많은 사람들이 이 요구 사항을 쉽게 만족할 수 있도록 도움을 주기 위해 블로그를 공유했다.

많은 분들이 댓글로 도움을 받았다고 댓글을 달아 주셔서 큰 보람을 느꼈다. 나 역시 여러 글들을 통해 많은 도움을 받았기에, 이것이 우테코에서 바라는 상호 성장의 모습이 아닐까 하는 생각이 들었다.



2. 확장성과 유지보수성을 고려한 코드를 작성했는가?

이번 과제에서 확장성과 유지보수성을 고려하려고 노력했지만, 솔직히 말해서 잘 적용했다고 자신 있게 말할 수 없다.

특히 이번 미션에서 인터페이스를 사용하여 코드를 작성했지만, 과제를 제출하고 내가 작성한 코드를 다시 보면서 이상한 점을 느꼈다.

아래와 같은 인터페이스를 정의했는데:

public interface Calculator {
    long sum(List<Long> numbers);
}
public class AdditionCalculator implements Calculator {

    @Override
    public long sum(List<Long> numbers) {
        return numbers.stream()
                .mapToLong(Long::longValue)
                .sum();
    }
}

나는 AdditionCalculator처럼 더하기를 담당하는 Calculator를 만들었고, 추후에 뺄셈(-), 곱셈(*), 나눗셈(/) 등을 담당하는 다른 Calculator들을 만들고 싶었다. 하지만 인터페이스에서 long sum(List numbers);로 정의했기 때문에, "덧셈"만을 여러 방식으로 구현하게끔 작성된 셈이 되었다.

이 부분에서 잘못 설계한 것을 깨달았다. 다른 계산 방식들을 추가하고 싶었다면, sum이라는 이름 대신 좀 더 일반적인 이름으로 인터페이스를 정의했어야 했고, 덧셈, 뺄셈, 곱셈 같은 다양한 계산 로직을 유연하게 확장할 수 있는 구조를 설계했어야 했다.

예를 들어, calculate와 같이 보다 범용적인 이름을 사용했더라면, 다양한 계산 방식을 추가할 때 인터페이스가 더 적합하게 사용되었을 것 같다:

  public interface Calculator {
    long calculate(List<Long> numbers);
}

이번 과제를 통해 단순히 "인터페이스를 사용하면 확장성이 좋아진다"는 이유로 남용하기보다는, 왜 사용하는지 정확히 알고 사용해야겠다는 것을 느꼈다.



3. 객체지향 프로그래밍 원칙을 체계적으로 적용해 객체가 자신의 책임을 명확히 했는가?

미션을 수행하면서 객체 지향 프로그래밍 원칙을 명확히 적용하고자 노력했다.

단일 책임 원칙(SRP)

이 원칙은 하나의 클래스는 하나의 책임만 가져야 한다는 원칙으로, 이를 통해 코드의 응집도를 높이고 변화에 따른 영향을 최소화할 수 있다.

Delimiters 클래스: Delimiters 클래스는 오직 구분자 관리를 책임지며, 기본 구분자와 커스텀 구분자를 관리하는 역할을 담당한다. 구분자를 추가하거나 조회하는 로직만을 포함하고, 그 외의 로직은 포함하지 않았다.

CustomDelimiterExtractor와 CustomDelimiterRemover 클래스: 구분자 추출과 제거는 각각 CustomDelimiterExtractorCustomDelimiterRemover 클래스에서 담당한다. 두 클래스 모두 구분자 처리에 대한 명확한 책임을 부여 했다. 이를 통해 구분자 관련 로직의 수정이 필요할 경우 다른 클래스에 영향을 주지 않고도 변경할 수 있다.

하지만 2. 확장성과 유지보수성을 고려한 코드를 작성했는가?에서 언급한 문제로 인해, 다른 객체 지향 원칙들은 제대로 지켜지지 못한 것 같다.

OCP(개방-폐쇄 원칙)의 적용 문제

Calculator 인터페이스를 사용하면서 OCP(개방-폐쇄 원칙)을 적용하려 했지만, 결과적으로 덧셈만을 처리하는 인터페이스로 제한되어 다른 계산 방식을 추가할 수 없는 구조가 되었다.



이번 과제를 마무리 하며

이번 과제를 통해, 내가 아직 배울점이 많다는 것을 느꼈다. 다음 과제들에서 이번에 부족했던 점을 보완하고 더 성장하고 싶다!! 화이팅 🔥🔥

profile
안녕하세요!

0개의 댓글