> [[우테코 7기 프리코스] 2주차 – 자동차 경주](https://velog.io/@grayson1999/%EC%9A%B0%ED%85%8C%EC%BD%94-7%EA%B8%B0-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-2%EC%A3%BC%EC%B0%A8-%EC%9E%90%EB%8F%99%EC%B0%A8-%EA%B2%BD%EC%A3%BC) <
벌써 2주가 지나고 3주차의 시작인 화요일이 되었다. 날씨가 오락가락하니 모두 감기 조심하길 바란다. 이렇게 간단한 인트로로 글을 시작하는 것이 어색하지만, 내 이야기를 적어 올리는 데 도움을 준다. 저번 주 회고를 생각하며 이번 주 피드백을 하려고 한다. 공통 피드백이기 때문에 내 상황에 맞게 이해하기 위해 해당 피드백을 정리해 보았다.
회고
2주차의 학습 목표는 함수 분리와 테스트 도구의 사용법을 익히는 것이었다. 미션 내용에서도 강조했듯, 이번에는 테스트 도구 사용법을 공부하고 더 나아가 디버깅 도구에 대한 사용법도 알아보았다.
다른 사람과 비교하다 보면 조바심이 생길 수 있다. 어제의 나와 비교하며 자신의 속도에 맞추어 마무리하는 것을 목표로 삼아 보자. 좋은 프로그래머로 성장하는 중요한 역량을 키우는 과정임을 기억해야 한다.
이번 미션을 진행하면서 가장 신경 쓰였던 부분이다. 사실 1주차 회고에서도 언급했던 내용이다. 남들과 비교하면서 “내가 이 정도면 합격할 수 있을까? 조금 더 깊은 지식을 원하는 건 아닐까?” 같은 사소한 고민에 빠져 중요한 부분을 놓치고 있었다. 중요한 것은 나의 속도이다. 내가 정말 이해하고 있는지, 어떤 부분이 더 중요한지 먼저 생각하고, 합격이라는 목표보다 나의 성장이 더 중요하다고 되뇌며 노력하지만, 사람의 욕심은 어쩔 수 없는 것 같다. 이번 프로젝트도 나의 성장과 이해를 중점 두고 남은 프로젝트를 진행할 예정이다.
추가로, 메타인지를 위한 최고의 도구 중 하나는 회고이다. 회고의 시간을 소홀히 하지 않고 정직하게 내가 모르는 부분을 인정하며 그 부분에 대해 공부하고 이해하려고 노력할 것이다.
공통 피드백
문서화
– README.md를 상세히 작성한다
>README.md는 프로젝트의 개요를 소개하는 중요한 문서이다. 이 문서에서는 해당 프로젝트가 어떤 내용인지, 주요 기능이 무엇인지 명확하게 설명해야 한다.
나는 문서화에 많은 시간을 사용하는 편이다. 어떤 프로젝트든, 심지어 개발과 관련이 없는 것들도 메모하는 습관이 있다. ‘신정철 – 메모 습관의 힘’이라는 책을 읽고 지금도 그 내용을 실천 중인데, 시간을 내어 꼭 읽어보기를 추천한다. 사람은 기억력이 좋지 않으며, 점점 더 많은 일을 동시에 하게 되어 기억하기가 더욱 어려워진다. 그래서 어떠한 프로젝트에서 적어도 미래의 나에게 설명하고 이해하기 위해 간단한 매크로라도 README를 작성하는 습관이 있다.
하지만 이번 프로젝트를 시작하면서 README 작성에도 컨벤션이 있을 것이라는 생각이 들었다. 비록 컨벤션이 아니더라도 좋은 작성법이 분명 존재할 것 같다. 이번 기회를 통해 README 작성법을 알아보려 한다.
[[codingPractices] README 작성법](https://velog.io/@grayson1999/codingPractices-README-%EC%9E%91%EC%84%B1%EB%B2%95)
– 기능 목록을 재검토한다
>기능 목록을 작성할 때 클래스 설계나 메서드 구현과 같은 세부 사항은 포함하지 않는다. 클래스 이름, 메서드 시그니처, 반환값 등은 언제든지 변경될 수 있기 때문이다.
따라서 구현해야 할 기능 목록에 초점을 맞추되, 정상적인 경우와 예외 상황도 함께 정리해야 한다. 예외 상황은 초기 단계에서 파악하기 어려울 수 있으므로, 기능 구현 중에 지속적으로 업데이트하는 것이 중요하다.
이번 프로젝트에서 가장 의문이 드는 부분은 기능 목록이다. 1주차 피드백에서는 기능을 세분화하고 개발 순서 및 버전 명을 정해 기능 목록을 작성했다. 2주차에는 1주차 피드백을 바탕으로 간략한 세분화와 요구 사항을 정리하여 기능 목록을 작성했으나, 아쉬운 점은 2주차 기능 목록을 업데이트하지 않았고 예외 상황도 정리하지 않았다는 것이다. 이번 피드백을 통해 기능 목록의 목적을 알게 되었다. 이런 뒤죽박죽 상황 속에서 더 이상의 의문이 생기기 전에 아래에서 기능 목록을 정리해 보겠다.
– 기능 목록을 업데이트한다
>README.md 파일의 기능 목록은 구현 과정에서 변경될 수 있다. 처음부터 모든 기능을 완벽하게 정리해야 한다는 부담을 느끼기보다는, 기능을 구현하는 동안 문서를 지속적으로 업데이트하는 것이 중요하다. 이를 통해 죽은 문서가 아닌 살아있는 문서로 유지될 수 있다.
처음에는 완벽한 계획이 정답이라고 생각했지만, 이는 현실적으로 불가능하다. 특히 처음 해보는 프로젝트에서는 필요한 기능이나 예외를 알기 어렵다. 이 문제를 해결하는 방법은 지속적인 업데이트이다. 완벽한 기능 목록을 작성하는 것도 중요하지만, 더 중요한 것은 변경 사항을 업데이트하여 문서를 계속 살아 있게 만드는 것이다. 따라서 완벽한 기능 목록 작성에 부담을 느끼기보다는 업데이트에 신경 쓰는 것이 좋다.
[[codingPractices] 기능 목록 작성 규칙](https://velog.io/@grayson1999/codingPractices-%EA%B8%B0%EB%8A%A5-%EB%AA%A9%EB%A1%9D-%EC%9E%91%EC%84%B1-%EA%B7%9C%EC%B9%99)
코딩 규칙
- 값을 하드 코딩하지 않는다
> 코드 내에서 문자열이나 숫자 값을 하드 코딩하지 않는다. 대신 상수(static final)를 정의하고 의미 있는 이름을 부여하여 해당 값이 어떤 역할을 하는지 명확히 드러내야 한다.
상수 (Constant)
- 정의: 변하지 말아야 할 데이터를 저장하는 수단으로, 초기화 후 재할당이 불가능하다.
- 선언: static final
- 이유
- 프로그램이 실행되면서 값이 변하면 안 되는 경우
- 코드의 가독성을 높이고 싶은 경우
- 유지보수를 쉽게 하고자 하는 경우
static
- 정의: “정적인”, “고정된”이라는 의미로, 전역이라고 이해할 수 있다. 객체 생성 없이 사용할 수 있는 필드와 메서드를 생성할 때 사용된다.
- 용도: 공용 데이터를 다룰 때 유용하며, 여러 클래스에서 일관된 메시지를 반환할 수 있다.
- 특징:
- 모든 메서드를
static으로 선언하면 인스턴스 없이 호출할 수 있다. - 정적 메서드는 객체 참조 없이 사용 가능하며,
this를 사용할 수 없다. - 메서드 영역에 저장되어 Garbage Collector에 의해 삭제되지 않고 시스템 종료 시까지 남아 있다.
final
- 정의: “최종인”, “결정적인”으로, 값이 한번 저장되면 수정이 불가능하다.
- 용도: 생성자 주입 시 해당 변수를
final로 선언하여 인스턴스 변경을 방지한다. - 특징:
- 값 할당 전까지는 수정 가능하지만, 할당 후에는 변경할 수 없다.
final메서드는 재정의할 수 없으며,final클래스로 선언하면 상속할 수 없다.
예제
public class Flow {
private final FlowContext flowContext;
public Flow(FlowContext flowContext) {
this.flowContext = flowContext; // 변경 불가
}
}
static final
- 정의: “고정된”+”최종”으로, 상수를 선언할 때 사용된다. 상수는 변하지 않는 값을 의미한다.
- 용도: 하드 코딩을 피하고 의미 있는 변수를 할당하여 코드의 가독성을 높인다.
- 특징:
- 선언 시 반드시 값을 할당해야 하며, 이후에는 변경할 수 없다.
예제
public class Message {
private static final String START_MESSAGE = "게임을 시작합니다.";
public static void printStartMessage() {
System.out.println(START_MESSAGE);
}
}
참고 자료
[[Java] 자바의 상수(Constant), final 변수 정리 출처:[IT is True:티스토리]](https://ittrue.tistory.com/100)
[[Java] static, final, static final의 차이](https://squirmm.tistory.com/entry/Java-static-final-static-final%EC%9D%98-%EC%B0%A8%EC%9D%B4)
구현 순서도 코딩 컨벤션이다
>클래스는 상수, 멤버 변수, 생성자, 메서드 순으로 작성해야 한다.
class A {
// 상수(static final) 또는 클래스 변수
// 인스턴스 변수
// 생성자
// 메서드
}
클래스의 구성 요소
- 상수 (static final)
- 인스턴스 변수
- 생성자
클래스 A의 예시
class A {
// 클래스 변수 (상수)
static final int MAX_SPEED = 200; // 모든 객체가 공유하는 최대 속도이다.
// 인스턴스 변수
String color; // 자동차의 색상이다.
String model; // 자동차의 모델이다.
int speed; // 자동차의 현재 속도이다.
// 생성자
public A(String color, String model) {
this.color = color; // 인스턴스 변수에 색상 값 할당한다.
this.model = model; // 인스턴스 변수에 모델 값 할당한다.
this.speed = 0; // 초기 속도는 0이다.
}
// 메서드: 속도를 증가시키는 기능
public void accelerate(int increment) {
if (speed + increment <= MAX_SPEED) {
speed += increment; // 속도를 증가시킨다.
System.out.println(model + "의 현재 속도: " + speed + " km/h이다.");
} else {
System.out.println(model + "은 최대 속도를 초과할 수 없다.");
}
}
}
– 변수 이름에 자료형은 사용하지 않는다
>변수 이름을 정할 때, 자료형이나 자료 구조를 포함하지 않고 의미를 명확하게 드러내도록 해야 한다. 이를 통해 코드 작성 시점에 변수의 용도를 자연스럽게 이해할 수 있게 한다.
String userName; // 자료형 없이 의미 명확
int[] scores; // 자료형 없이 의미 명확
– 한 메서드가 한 가지 기능만 담당하게 한다
>함수의 길이가 길어지면 여러 기능을 하나의 함수에서 처리하려는 신호일 수 있다. 이 경우, 각 기능(예: 안내 문구 출력, 사용자 입력 처리, 유효값 검증 등)을 개별 함수로 분리하여 코드의 가독성과 유지보수성을 향상시켜야 한다.
– 메서드가 한 가지 기능을 하는지 확인하는 기준을 세운다
>여러 메서드에서 중복되는 코드가 있다면 이를 별도의 메서드로 분리하는 것을 고려해야 한다. 또한, 메서드의 길이가 길어질수록 여러 기능을 포함할 가능성이 크므로, 15라인을 넘지 않도록 구현하는 것이 좋다. 이렇게 하면 의식적으로 메서드를 분리하는 연습을 할 수 있다.
지켜야 할 원칙
1. 단일 책임 원칙: 한 메서드는 하나의 기능만 담당하도록 한다.
2. 코드 중복 제거: 여러 메서드에서 중복되는 코드는 별도의 메서드로 분리한다.
3. 메서드 길이 제한: 메서드는 15라인 이하로 유지하여 여러 기능을 포함하지 않도록 한다.
테스트
– 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다
>테스트 작성은 기능의 정확성을 점검하는 것뿐만 아니라, 코드에 대한 즉각적인 피드백을 제공한다. 이를 통해 구현한 기능의 문제를 신속하게 발견할 수 있으며, 코드의 구조와 의도를 명확히 이해하는 데도 도움이 된다. 또한, 테스트는 학습 도구로 활용할 수 있는 장점이 있다.
테스트 코드를 작성하면 내가 의도한 대로 작동하는지 바로 확인할 수 있다. 작은 단위마다 테스트를 하기에 수정한 부분에 대해 System.out.println을 사용해 확인하는 번거로움이 줄어든다. 예를 들어, 예외 처리를 할 때는 예외가 발생할 것 같은 입력을 넣고 컴파일하여 확인하는 작업이 필요했지만, 테스트 코드는 각 함수마다 확인할 수 있어 시간 절약이 된다.
이로 인해 개발 시간이 확실히 줄어들고, 계획적으로 코드를 작성하게 된다. 테스트 함수의 이름을 고려해야 하는 단점이 있지만, 익숙해지면 이 단점을 극복할 수 있다고 생각한다.
– 처음부터 큰 단위의 테스트를 만들지 않는다
>테스트의 핵심 목적 중 하나는 코드에 대해 빠르고 자주 피드백을 받는 것이다. 큰 단위의 테스트를 처음부터 작성하면 코드의 문제를 발견하기까지 시간이 오래 걸린다. 따라서 문제를 작게 나누어 핵심 기능부터 작은 테스트를 만드는 것이 효과적이다.