프리코스 1주차에 참여하며

1주차 과제인 만큼 최고의 능력을 끌어내려는 것보다 기본적인 내용을 구현하는 목적으로 이런 과제를 내주신 것 같다. 특히 기능적 요구 사항은 익숙하더라도 과제 진행 요구사항, 프로그램 요구 사항은 개인적으로 처음 접해본 부분이어서 좀 헤맨 감이 있다. 기존 진행해온 프로젝트에서 커밋 메세지 작성 규칙을 지키는 것이나 자바 코드 컨벤션을 지키는 것과 같은 부분은 없었기 때문이다. 정보처리기사 실기 시험이 끝난지 얼마 안된 시점에서 이 부분을 적용해보면서 제출에 성공한 것만으로도 나에겐 의미있는 시간이었다.
커밋 메세지 작성 규칙
진행한 작업의 유형(작업 법위) : 설명
📌 Allowed <type>작업 유형은 다음과 같다.
- feat (feature)
- fix (bug fix)
- docs (documentation)
- style (formatting, missing semi colons, …)
- refactor
- test (when adding missing tests)
- chore (maintain)
제출 내용
가장 먼저, 주어진 과제의 기능 요구 사항에 따라 기능을 분석하고 이에 따라 설계하였다.
📌 기능 분석
- 하나의 문자열로 입력이 들어오면, 파싱 후 잘못된 값을 입력하였는지 검증해야한다.
- 입력값에 대해 구분자와 숫자 파싱 후 그 외의 값을 잘못된 값으로 간주
- 기본 구분자 : 쉼표, 콜론
- 커스텀 구분자 : 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자
- 잘못된 값 있을 시 에러 발생 후 종료
- 잘못된 값이 없으면 숫자값들을 모두 더하는 연산 수행
- 값을 출력
📌 설계시간이 촉박했기 때문에 가장 익숙했던 MVC 패턴을 선택하였고, 리팩토링을 하더라도 일단 제출을 위한 기본적인 기능 구현이 우선이라고 생각해서 인터페이스 활용이나 메소드 분리를 하지 않았다. 총 클래스는 아래 3개와 메인 어플리케이션까지 해서 총 4개뿐이다.
Model
Calculator 클래스 : 파싱, 검증, 합계 계산
View
ConsoleView 클래스 : 사용자에게 요청 및 결과를 출력하는 클래스
Controller
CalculatorController 클래스 : 모델과 뷰를 연결
기능 구현 후 커스텀 구분자로 작성된 숫자 합계 테스트 케이스와 잘못된 값이 들어왔을 때 예외를 발생시키는 테스트 케이스를 충족시키기 위해 등 몇 가지 최소한의 에러 처리 코드를 추가하였다.
- 입력값이 없을 때
- 입력 숫자가 1개일 때
- 커스텀 구분자가 명시되었을 때 파싱
- 음수 값이 들어왔을 때(양수 값만을 계산하는 프로그램이다.)
클린코드 사용 지표 달성 여부 🍀
사실상 구현에 치중하여 이 항목을 체크하는 과정을 제대로 못했던 것 같다. 늦게라도 문제를 되짚어보고 각 지표가 필요한 이유를 정리해보자.
- 한 메서드에 오직 한 단계의 들여쓰기(indent)만 허용했는가?
: 메서드는 가능한 간결하고 단순해야하는데, 여러 개의 들여쓰기가 발생하면 복잡성이 증가한다.
- Application
- Calculator
- ConsoleView
- CalculatorController
- else 예약어를 쓰지 않았는가?
: 조건이 참일 때만 작업을 수행하고 그렇지 않을 경우 아무것도 하지 않도록 설계하는 것이 코드의 흐름이 명확한 방법이다.
- Application
- Calculator : 커스텀 구분자를 판별하는 과정에서 if- else if- else문을 사용했다
- ConsoleView
- CalculatorController
- 모든 원시값과 문자열을 포장했는가?
: 포장하여 사용하는 것이 더 안전하고 유연하다. 해당 값의 유효성 검사나 관련 동작을 메서드로 따로 정의하여 가독성과 안전성이 높아진다.
- Application : 필드 내 활용 변수 없음
- Calculator : 전체 계산 과정에서 문자열을 Int형으로 변환하여 계속 사용하고 있다.
- ConsoleView : 문자열 입력 및 출력만 진행하고 있음
- CalculatorController : 계산 결과값을 Int형의 지역변수로 활용하고 있다.
- 콜렉션에 대해 일급 콜렉션을 적용했는가?
: 컬렉션(예:List,Set)을 직접 사용하지 않고 이를 감싸는 클래스를 만들어 사용
일급 콜렉션은 컬렉션에 대한 동작(메서드)와 데이터(컬렉션 항목)을 한 곳에 모아서 응집력이 높아진다. 이를 통해 해당 컬렉션에 어떤 데이터가 포함되는지, 어떤 작업을 할 수 있는지 명확히 알 수 있다. 또한, 외부에서 직접 접근할 수 없도록 하여 캡슐화를 강화할 수 있으며 콜렉션 내 데이터 유효성 검사를 포함시켜 조건에 만족하는 값만 포함할 수 있도록 설정할 수 있다. (데이터 무결성 보장)
-
- Application : 필드 내 활용 변수 없음
- Calculator : 전체 계산 과정에서 문자열 배열과 Int형 배열을 계속 사용하고 있다.
- ConsoleView : 문자열 입력 및 출력만 진행하고 있음
- CalculatorController : 계산 결과값을 int 형 지역변수로 사용하고 있다.
- 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가?
: 인스턴스 변수의 수를 줄이면 클래스가 복잡해지지 않으며 상태 관리도 용이하여 유지보수성이 높아진다. 변수가 많을 수록 동작 예측 및 변수 변경, 초기화, 연산시에도 고려할 사항이 많아지는 것. 또한, 다른 클래스에서도 쉽게 재사용 가능한 컴포넌트가 되므로 상태 의존성이 적다.
- Application
- Calculator
- ConsoleView
- CalculatorController
- getter/setter 없이 구현했는가?
- 핵심 로직을 구현하는 도메인 객체에 getter/setter를 쓰지 않고 구현했는가?
- 단, 데이터 전송을 위한 DTO 객체는 허용한다.
- 외부 코드가 객체 상태를 직접 수정하는 것을 피하고 필요시 메소드를 통해 접근하도록 하면 객체 내부 상태를 보호할 수 있다. 또한 코드의 의도도 명확해져 파악하기 쉽다.
- 해당 없음
- 메소드의 인자 수를 제한했는가?
- 4개 이상의 인자는 허용하지 않는다. 3개도 가능하면 줄이기 위해 노력해 본다.
- 메서드 인자 수가 많아지면 코드의 직관성이 떨어진다.
- Application : 해당 없음
- Calculator : 최대 2개
- ConsoleView : 최대 1개
- CalculatorController : 해당 없음
- 코드 한 줄에 점(.)을 하나만 허용했는가?
- 디미터(Demeter)의 법칙(“친구하고만 대화하라”)을 지켰는가?
- 예를 들어 location.current.representation.substring(0, 1)와 같이 여러 개의 점(.)이 등장하면 리팩토링할 부분을 찾아본다.
- 객체는 자신이 직접 알고 있는 객체와만 상호작용 해야한다.
- Application : 해당 없음
- Calculator : Stirng[]을 int[]로 바꾸는 과정에서 여러 “.”이 등장했다.
- ConsoleView : 잘 지키고 있다.
- CalculatorController : 잘 지키고 있다.
- 디미터(Demeter)의 법칙(“친구하고만 대화하라”)을 지켰는가?
- 메소드가 한가지 일만 담당하도록 구현했는가?
- 단일 책임 원칙(SRP)을 지키면 하나의 책임만 가지므로 유지보수성과 가독성, 테스트 용이성이 높다. 또 재사용성이나 확장성이 증가하여 코드 중복을 줄이고 최소한의 수정으로 확장이 가능하다.
- Application : 프로그램 실행
- Calculator
- calc(Stirng input) : 유효성 검사와 합계 계산 메서드를 호출하여 최종값을 얻음
- sum(int[] arr) : 정수 배열값들의 합계 구하기
- validateInput(String input) : 구분자 종류 확인, 없을시 에러 발생 기능. 다만 커스텀 구분자일 경우 문자열에서 해당 구분자를 제거하는 작업이 있어 단일 책임 원칙을 위반하고 있다.
- extractCustomDelimiter(String input) : 문자열에서 커스텀 구분자 내용 추출
- parseNumbers(String input, String delimiter) : 문자열 → 문자열 배열
- convertToIntArray(String[] stringNumbers) : 문자열 배열 → 정수 배열
- ConsoleView
- request() : 문자열 출력 및 입력값 요청 뿐만 아니라 빈 값을 입력하였을때의 검증도 하고 있어 단일 책임 원칙을 위반하고 있다.
- printResult(int result) : 결과값을 출력
- printError(String message) : 에러 내용과 종료문을 출력
- CalculatorController
- run() : 뷰와 모델을 연결
- 단일 책임 원칙(SRP)을 지키면 하나의 책임만 가지므로 유지보수성과 가독성, 테스트 용이성이 높다. 또 재사용성이나 확장성이 증가하여 코드 중복을 줄이고 최소한의 수정으로 확장이 가능하다.
- 클래스를 작게 유지하기 위해 노력했는가?
- 다른 클래스를 의존하는 경우, 의존성 주입으로 결합도를 낮춘다. 관련 기능을 묶은 새로운 클래스로 특정 기능에 대한 추상화를 진행한다.
- 클래스 기능을 인터페이스로 정의하여 구현을 다른 클래스로 분리한다. 등
- 클래스가 작고 간결할수록 코드의 가독성과 유지보수성이 좋아진다.
- Calculator : 각기 다른 기능의 메소드 6개(합계 계산, 유효성 검증, 구분자 추출, 문자열 파싱, 문자열 형변환)가 한번에 위치하고 있다.
- ConsoleView : 메소드 3개가 전부이다.
- CalculatorController : ConsoleView, Calculator 클래스를 필드에서 직접 생성하고 있다.
- 다른 클래스를 의존하는 경우, 의존성 주입으로 결합도를 낮춘다. 관련 기능을 묶은 새로운 클래스로 특정 기능에 대한 추상화를 진행한다.
📌 클린코드 사용 지표 달성 여부 종합
🔍 공통 피드백 확인
2차 과제가 올라오기 전에는 1차 과제의 공통 피드백이 올라왔다. 이후 과제에서 내가 유의미하게 적용해볼 피드백 내용을 정리해보았다. 아래 내용과 클린 코드 작성 규칙을 종합하여 다음 과제 제출 전 개인 체크 리스트로 활용하고자 한다. 이후 과제를 진행하면서 계속 내용을 수정해가며 나에게 딱 맞는 체크 리스트가 될 것으로 기대한다.
| 주제 | 내용 |
|---|---|
| Git | 기본적인 Git 명령어를 숙지한다. |
| Git으로 관리할 자원을 고려한다. | |
| 해당 커밋에서 수행된 작업을 이해할 수 있도록 커밋 메시지를 의미 있게 작성한다. | |
| 풀 리퀘스트를 만든 후에는 닫지 말고 추가 커밋을 한다. | |
| 이미 풀 리퀘스트를 생성하면 변경을 위해 새 풀 리퀘스트를 만들 필요가 없다. 변경이 필요한 경우 추가 커밋을 하면 자동으로 반영된다. | |
| 과제 제출 | 과제를 제출하기 전에 과제 진행 요구 사항, 기능 요구 사항, 프로그래밍 요구 사항을 모두 충족하였는지 다시 한번 확인한다. 이러한 요구 사항들은 미션마다 다르므로 항상 주의 깊게 읽어본다. |
| 코드 포맷 | if, for, while문 사이의 공백도 코딩 컨벤션이다. |
| 코드 포매팅을 사용한다 | |
| 주석 | 변수 이름, 함수(메서드) 이름을 통해 어떤 의도인지가 드러난다면 굳이 주석을 달지 않는다. |
| 의도를 드러내기 힘든 경우에만 주석을 다는 연습을 한다. | |
| 변수 활용 | 배열 대신 컬렉션을 사용한다. |
| 이름을 통해 의도를 드러내며 축약하지 않는다. |
보완 진행 🐈
클린 코드 작성 규칙을 익히고 코드간 의존성을 낮추기 위해서 로컬에서라도 클린코드 규칙 적용을 적용한 리팩토링을 진행하려고 한다. 리팩토링 이후 가독성 정도의 향상을 판단하기 위해 클린코드 작성 규칙을 전부 준수하는지 검토하고, 코드 라인수가 어떻게 변화했는지를 참조하여 비교를 해보고자 한다.
코드 라인수 측정의 단위
코드라인은 가독성을 직접적으로 판단할 순없지만, 유지보수를 고려하지 않은 채로 작성하였던 코드인만큼 적정 라인수로 맞추는 것은 필요할 듯하여 선택하였다.
코드 라인의 기준을 무엇으로 잡을 지 보았을 때, 내가 지키지 못했던 클린코드 규칙 중 단일책임원칙과 클래스를 작은 단위로 유지해야하는 내용을 활용하기로 하였다. 이에 따라 패키지, 클래스, 메소드 단위로 코드 라인수를 비교할 것이다.
- 패키지 단위
- 패키지 안의 라인 수를 통해 리팩토링 정도를 체크하면서 각 세부 단위별 변화를 비교참조할 수 있는 단위로 사용한다.
- 클래스 단위
- 클래스 라인수는 간접적으로 해당 클래스가 얼마나 많은 책임을 지고 있는 지를 보여준다.
- 일반적으로 100~200라인을 초과하지 않도록 유지하는 것이 좋다.
- 메소드 단위
- 메소드의 단일 책임 원칙을 위해 20~30라인 이내로 유지하는 것이 가독성 유지하면서 필요한 로직을 수행할 수 있는 단위라고 한다.
가독성 평가 도구 선정
📌 선정 과정
plugins {
id 'java'
id 'pmd'
}
pmd {
toolVersion = '7.0.0'
ruleSets = []
ruleSetFiles = files("config/pmd/intellij-java-wooteco-style.xml") // 규칙 파일 경로
ignoreFailures = false //규칙 미준수시 빌드 실패
reportsDir = file("config/pmd/reports")
}
IntelliJ에서 코드 라인 수 체크하는 방법
- Statistic Plugin을 인텔리제이 마켓플레이스에서 설치한다.(File > Settings(또는 Preferences on Mac) > Plugins)
- 다양한 종류가 있는데 나는
MetricsReloaded를 설치했다. 패키지 단위, 클래스 단위, 메소드 단위로 CLOC, JLOC, LOC, NCLOC 등의 지표를 확인할 수 있다. - 실행 방법은 인텔리제이 내 검색에서 metrics calculation 선택하면 된다.
📌 코드 라인 지표 종류
- CLOC(Comment Lines of Code) : 주석 코드 라인 수
- JLOC(Java Lines of Code) : 자바 코드 라인 수 → 실제 실행되는 코드
- LOC(Lines of Code) : 전체 코드 라인 수(주석 + 비어있는 라인 등)
- LOCp(Lines of Code in Procedures) : 절차나 메서드에 있는 코드라인 수
→ 기능 단위 리뷰 가능 = 특정 메서드나 기능 복잡성 이해에 도움- LOC(rec) (Lines of Code (Recursive)) : 재귀적으로 호출되는 코드의 라인 수
- NCLOC(Non-Comment Lines of Code) : 주석 제외한 코드 라인 수
- RLOC(Real Lines of Code) : 주석 포함된 코드 라인 수 → 코드의 전체 분량
- 이 값이 낮을 수록 많은 줄 수에 비해서 실제 실행되는 코드가 적다는 것을 의미한다. (최적화 필요)
보완 결과 📢
리팩토링 BEFORE
리팩토링 전의 프로젝트 파일 구조는 다음과 같다.
MVC로 나뉘긴 했지만 Calculator 클래스에 가장 많은 내용이 담겨있다.
calculator
│
├── src
│ └── main
│ ├── java
│ │ └── calculator
│ │ ├── controller
│ │ │ └── CalculatorController.java
│ │ ├── model
│ │ │ └── Calculator.java
│ │ └── view
│ │ └── ConsoleView.java
│ └── resources
│
└── test
└── java
└── calculator
└── ApplicationTest.java
테스트 코드는 제외하였다.
패키지, 클래스 단위
| 패키지/클래스 | CLOC | JLOC | LOC | LOCp | LOC(rec) | NCLOC |
|---|---|---|---|---|---|---|
| src.main.calculator | 0.0 | 0.0 | 94.0 | 94.0 | 94.0 | 94.0 |
| Total | 0.0 | 0.0 | 94.0 | 94.0 | - | 94.0 |
| Average | 0.00 | 0.00 | 23.50 | 23.50 | - | 23.50 |
| Application | 0.0 | 0.0 | 6.0 | - | - | - |
| Calculator Controller | 0.0 | 0.0 | 13.0 | - | - | - |
| Calculator | 0.0 | 0.0 | 49.0 | - | - | - |
| ConsoleView | 0.0 | 0.0 | 17.0 | - | - | - |
| Total | 0.0 | 0.0 | 85.0 | - | - | - |
| Average | 0.00 | 0.00 | 21.25 | - | - | - |
- 전체 라인 수가 94밖에 되지 않는 작은 프로젝트이다.
- Calculator 클래스가 가장 높은 49.0LOC로 전체 코드의 상당부분을 차지하고 있으므로 복잡성을 유발할 가능성이 가장 높은 클래스였음을 의미한다.
메소드 단위
| 클래스 | 메소드 | CLOC | JLOC | LOC | NCLOC | RLOC |
|---|---|---|---|---|---|---|
| Application | main() | 0.0 | 0.0 | 4.0 | 4.0 | 66.67% |
| Calculator Controller | run() | 0.0 | 0.0 | 9.0 | 9.0 | 69.23% |
| Calculator | calc(String) | 0.0 | 0.0 | 7.0 | 7.0 | 14.29% |
| convertToIntArray(String[]) | 0.0 | 0.0 | 11.0 | 11.0 | 22.45% | |
| extractCustomDelimiter(String) | 0.0 | 0.0 | 5.0 | 5.0 | 10.20% | |
| parseNumbers(String, String) | 0.0 | 0.0 | 7.0 | 7.0 | 14.29% | |
| sum(int[]) | 0.0 | 0.0 | 3.0 | 3.0 | 6.12% | |
| validateInput(String) | 0.0 | 0.0 | 14.0 | 14.0 | 28.57% | |
| ConsoleView | printError(String) | 0.0 | 0.0 | 3.0 | 3.0 | 17.65% |
| printResult(int) | 0.0 | 0.0 | 3.0 | 3.0 | 17.65% | |
| request() | 0.0 | 0.0 | 9.0 | 9.0 | 52.94% | |
| Total | 0.0 | 0.0 | 75.0 | 75.0 | - | |
| Average | 0.00 | 0.00 | 6.82 | 6.82 | 20.60% |
- Claculator 클래스 내 메소드들이 다양한 LOC값을 갖는 것을 확인할 수 있다. 특히 validateInput(String)메소드가 14.0 LOC로 가장 길다. 내용을 분석하여 리팩토링할 필요가 있다.
- convertToIntArray(String[]) 메소드 역시 11.0 LOC로 다른 메소드에 비해 긴 편이므로 필요 없는 중복 코드가 있는지 등을 점검할 필요가 있음을 확인할 수 있다.
- RLOC 비율이 낮은 calc(String)과 sum(int[]) 메소드는 14.29%, 6.12%로 낮기 때문에 코드 개선이 필요함을 시사한다.
리팩토링 AFTER
기존 계획에는 가독성 평가 도구를 활용하기 위해서 pmd를 선정하고 실행해보았으나 규칙 파일을 별도로 설정해야함에 따라 리팩토링 전의 전체 코드가 94밖에 되지 않는 프로젝트에서 과도한 적용이라고 생각해서 하지 않고 코드라인 지표를 통한 전후만 살펴보고자 한다. (현재 우선순위는 2차 과제 제출이 더 높다.)
리팩토링 후의 프로젝트 파일 구조는 다음과 같다.
calculator
│
├── src
│ └── main
│ ├── java
│ │ └── calculator
│ │ ├── controller
│ │ │ └── CalculatorController.java
│ │ ├── model
│ │ │ ├── Calculator.java
│ │ │ ├── Parser.java
│ │ │ └── Validator.java
│ │ └── view
│ │ ├── InputView.java
│ │ └── OutputView.java
│ └── resources
│
└── test
└── java
└── calculator
├── ApplicationTest.java
└── ValidatorTest.java
패키지, 클래스 단위
| 패키지/클래스 | CLOC | JLOC | LOC | LOCp | LOC(rec) | NCLOC |
|---|---|---|---|---|---|---|
| src.main.calculator | 0.0 | 0.0 | 8.0 | 8.0 | 150.0 | 8.0 |
| src.main.calculator.controller | 0.0 | 0.0 | 25.0 | 25.0 | 25.0 | 25.0 |
| src.main.calculator.model | 0.0 | 0.0 | 95.0 | 95.0 | 95.0 | 95.0 |
| src.main.calculator.view | 0.0 | 0.0 | 22.0 | 22.0 | 22.0 | 22.0 |
| Total | 0.00 | 0.00 | 150.00 | 150.00 | - | 150.00 |
| Average | 0.00 | 0.00 | 37.50 | 37.50 | - | 37.50 |
| calculator.Application | 0.0 | 0.0 | 6.0 | - | - | - |
| controller. Calculator Controller | 0.0 | 0.0 | 18.0 | - | - | - |
| model.Calculator | 0.0 | 0.0 | 13.0 | - | - | - |
| model.Parser | 0.0 | 0.0 | 37.0 | - | - | - |
| model.Validator | 0.0 | 0.0 | 37.0 | - | - | - |
| view.InputView | 0.0 | 0.0 | 11.0 | - | - | - |
| view.OutputView | 0.0 | 0.0 | 8.0 | - | - | - |
| Total | 0.00 | 0.00 | - | - | - | |
| Average | 0.00 | 0.00 | - | - | - |
- 전체 라인 수가 94에서 150으로 상승했다. 새로운 기능 추가는 없었으나 구조적으로 클래스와 메소드를 분할하면서 라인이 추가되었을 것 같다.
- Calculator 클래스가 가장 높은 49.0LOC로 전체 코드의 상당부분을 차지하고 있었으나 18.0LOC로 감소하였다. Parser, Validator 클래스로 분할되었기 때문임을 확인할 수 있다. 이를 통해 파싱과 유효성 검증이 계산기 비즈니스 로직 중 각 37.0LOC로 가장 높은 비중을 차지하고 있었음을 짐작할 수 있다.
- LOCp(주석 포함 코드라인 수)와 LOC(rec)(코드 재사용 라인 수)가 총 94라인에서 150라인으로 향상하였다. 여기서 주석은 모두 0이기 때문에 LOCp는 해석하지 않는다.
- LOC(rec)가 향상하였다는 것은 동일한 기능을 수행하는 코드가 여러 번 중복되지 않고 함수나 클래스로 잘 분리되도록 변경되었음을 의미한다.
메소드 단위
| 클래스 | 메소드 | CLOC | JLOC | LOC | NCLOC | RLOC |
|---|---|---|---|---|---|---|
| Application | calculator.Application.main(String[]) | 0.0 | 0.0 | 4.0 | 4.0 | 66.67 |
| Calculator Controller |
calculator.controller.CalculatorController.run() | 0.0 | 0.0 | 14.0 | 14.0 | 77.78% |
| Calculator | calculator.model.Calculator.calc(List) | 0.0 | 0.0 | 4.0 | 4.0 | 30.77% |
| calculator.model.Calculator.sum(List) | 0.0 | 0.0 | 7.0 | 7.0 | 53.85% | |
| Parser | calculator.model.Parser.parseCustomDelimiter(String) | 0.0 | 0.0 | 5.0 | 5.0 | 13.51% |
| calculator.model.Parser.parseDelimiter(String) | 0.0 | 0.0 | 12.0 | 12.0 | 32.43% | |
| calculator.model.Parser.parseInput(String) | 0.0 | 0.0 | 10.0 | 10.0 | 27.03% | |
| calculator.model.Parser.parseStringArr(List) | 0.0 | 0.0 | 5.0 | 5.0 | 13.51% | |
| calculator.model.Parser.replaceDelimiterMarker(String, String) | 0.0 | 0.0 | 3.0 | 3.0 | 8.11% | |
| Validator | calculator.model.Validator.isCustomDelimiter(String) | 0.0 | 0.0 | 3.0 | 3.0 | 8.11% |
| calculator.model.Validator.isLargerThanZero(List) | 0.0 | 0.0 | 7.0 | 7.0 | 18.92% | |
| calculator.model.Validator.isNullOrEmpty(String) | 0.0 | 0.0 | 3.0 | 3.0 | 8.11% | |
| calculator.model.Validator.isValidCharacter(String) | 0.0 | 0.0 | 3.0 | 3.0 | 8.11% | |
| calculator.model.Validator.isValidCustomDelimiter(String) | 0.0 | 0.0 | 3.0 | 3.0 | 8.11% | |
| calculator.model.Validator.isValidInput(String) | 0.0 | 0.0 | 14.0 | 14.0 | 37.84% | |
| InputView | calculator.view.InputView.request() | 0.0 | 0.0 | 9.0 | 9.0 | 81.82% |
| OutputView | calculator.view.OutputView.printError(String) | 0.0 | 0.0 | 3.0 | 3.0 | 37.50% |
| calculator.view.OutputView.printResult(int) | 0.0 | 0.0 | 3.0 | 3.0 | 37.50% | |
| Total | 0.0 | 0.0 | 112.0 | 112.0 | - | |
| Average | 0.0 | 0.0 | 6.22 | 6.22 | 23.14% |
- 기존 메소드는 11개였으나 증가하여 22개가 되었음을 통해 메소드가 분산되었음을 확인할 수 있다.
- 전체 메소드의 LOC는 75.0에서 112.0으로 대폭 증가하였다. 이를 통해 함수나 메소드가 명확해지고 기능이 많이 나누어졌음을 짐작할 수 있다.
- 특히, 새로 추가된 Parser.paresInput(String) 메소드의 LOC가 10.0으로
- 전체 메소드의 RLOC는 20.60%에서 23.14%로 소폭 증가하여 재사용 가능성이 소폭 향상되었음을 알 수 있다.
- 아마도 RLOC의 낮은 증가는 Service 등 인터페이스를 활용한 디자인 패턴이 적용되지 않아서 그럴 수 있을 것 같다.
트러블슈팅 😵💫
정규식 작성
허용가능한 구분자 정규식을 상수로 등록하고 하나의 정규식으로 활용하는 과정에서 다수의 숫자를 연달아 구분하여 입력했을 때 오입력으로 인식하고 IllegalArgumentException 에러가 발생하였다.
$, ^는 단순 문자열 안에서는 위치상 오류라는 내용이 떠서 \\로 이스케이프 처리를 한 상황
private static final String ALLOWED_CUSTOM_DELIMITERS = "!@#\\$%\\^&_+:;<>/|?";
private static final String ALLOWED_BASIC_DELIMITERS = ".,";
public static void isValidInput(String input) throws IllegalArgumentException {
//중략
if (isCustomDelimiter(input)) {
if (!isValidCustomDelimiter(input.substring(2, input.indexOf("\\n")))) {
throw new IllegalArgumentException("입력가능한 특수문자 : " + ALLOWED_CUSTOM_DELIMITERS);
}
input = input.substring(input.indexOf("\\n") + 2);
}
}
static boolean isValidCharacter(String input) {
return input.matches("^[0-9]+$[" + ALLOWED_CUSTOM_DELIMITERS + ALLOWED_BASIC_DELIMITERS + "]?$");
}
해결 ✨
- 정규식에서 문자열이 하나 이상의 숫자
[0-9]+로 시작해야하는 것을 필수로 검증하고 이후 구분자와 숫자는 세트로 있을 수도 있고 없을 수도 있다는 의미로()안에 작성 - 해당 구분자와 숫자는
*을 통해 0번 이상 반복될 수 있도록 작성하였다.
static boolean isValidCharacter(String input) {
return input.matches("^[0-9]+([" + ALLOWED_CUSTOM_DELIMITERS + ALLOWED_BASIC_DELIMITERS + "][0-9]+)*$");
}
정상 작동

회고
리팩토링 진행
처음에는 프로젝트를 전체적으로 다 뜯어고쳐야할 것 같다는 생각에 막막했고 원시 변수도 컬렉션으로 바꾸다보니 거의 모든 클래스들을 손대야 했던 것 같다. 또 리팩토링을 제출 직후 시작하지 않았어서 작성했던 기능을 다시 되새겨야하는 과정이 필요했다. 그래도 컨트롤러에서 흐름을 따라서 가보니 할만 했던 것 같다.
그래도 처음에만 버벅였지 새로운 기능 추가보다는 구조적 개선을 목적으로 했기 때문에 생각했던 것보다 작성하는데 그리 오래 걸리지 않았고, 테스트코드 추가하고 문제 해결하고 기록하는데에 절반 이상은 걸린 것 같다.
오히려 처음 리팩토링 지표와 평가 도구에 대해서 알아볼 때 가장 큰 시간이 들었다. 10/22 오후에 시작했으나 이렇게 딜레이 될 줄 몰랐다.
처음에는 계획했던 리팩토링 일정이 밀리면서 리팩토링 전후를 객관적인 지표를 통해서 확인한다는 목표가 너무 거창한 것은 아닌지, 그래도 이럴 때 아니면 언제 시간내서 이렇게 해보나, 만약 결과가 무의미하면 어쩌지 등등 많은 생각이 들었었다. 하지만, 끝까지 포기하지 않고 진행한 결과가 훨씬 유의미한 내용이었고 내가 진행한 리팩토링의 효과성을 수치로 직접 확인할 수 있는 것이 신기하고 뿌듯했다!
1주차 과제를 진행하며
리팩토링의 전과 후를 꼭 비교해보고 효과성을 체감해보고 싶다는 욕심에 처음 계획을 하면서 코드 라인 수 전후 측정과 PMD 분석까지 하는 과정에서 시간이 많이 잡아먹었다.
이 역시 데일리 회고(KPT)를 개인적으로 진행하다보니 깨달은 사실인데 완벽하게 하고 싶은 내 마음이 너무 강렬했던 것을 알게 되었다. 이전부터 다양한 것들을 진행할 때마다 완벽주의에 대한 욕구로 많은 것들을 더 잘 할 수 있었지만, 제한 시간이 있는 과제가 있고 그 외에도 할 일이 많은 시점에서는 완벽주의가 전혀 도움이 안된다는 생각이 들었다.
완벽주의 조절하기 💯
이 완벽주의를 조절하기 위해서 작업의 완료 기준을 명확히 정하는 방법과 기한을 설정하는 방법을 선택하기로 하고, 완벽함보다는 작업을 통해서 새로 배운 점이 무엇인지 잘 정리하고 내 것으로 남기는 것에 회고 작성에 목적을 두기로 했다. 앞으로는 회고를 작성할 때 내가 프리코스에서 배우고자하는 주제인 리팩토링과 코드 가독성 향상이 꼭 아니더라도 과제 진행 중에 새로 알게된 점을 중점으로 작성하고 마무리하고자 한다.
프리코스의 목적 달성 여부 ✅
지원서에 작성했던 목표는 “작성한 코드를 리팩토링하여 가독성과 효율성을 높이고 품질을 향상시키는 방법을 학습하는 것”이었다. 구체적으로 리팩토링을 통한 코드 품질을 향상시켜보고 전후 비교를 통해 중요성을 체감해보는 것 자체였기 때문에 완벽하게 달성했다! 👏🏻👏🏻👏🏻
'프리코스' 카테고리의 다른 글
| [프리코스 7기 백엔드] 4주차 과제 회고 :: 프리코스 참여를 마무리하며 (1) | 2025.01.03 |
|---|---|
| [프리코스 7기 백엔드] 3주차 과제 회고 :: 3주간의 변화 (1) | 2024.11.09 |
| [프리코스 7기 백엔드] 2주차 과제 회고 (+ JUnit, AssertJ, java.util.Comparator) (0) | 2024.10.29 |