🔥 커밋 메시지 컨벤션(Commit Message Convention)이란?
- Git 커밋 메시지를 일관된 형식으로 작성하기 위한 규칙
- 협업을 원활하게 하고, 변경 이력을 깔끔하게 관리할 수 있음
📝 커밋 메시지 컨벤션의 필요성
| 목적 | 설명 |
|---|---|
| 가독성 향상 | 변경 내용을 한눈에 파악할 수 있게 함 |
| 협업 효율성 | 팀원 간 이해를 높이고 충돌 가능성 감소 |
| 자동화 도구 연동 | changelog 자동 생성, semantic versioning 연동 가능 |
| 버전 관리 일관성 | 커밋 메시지로 변경 타입을 명확히 분류 |
🔧 커밋 메시지 구조 (예: Conventional Commits)
<type>(optional scope): <subject>
(blank line)
<body>
(blank line)
<footer>
feat(login): add OAuth login feature
사용자가 구글 계정으로 로그인할 수 있도록 OAuth2 인증을 추가했습니다.
BREAKING CHANGE: 기존 로그인 로직이 변경되었습니다. 프론트엔드도 수정이 필요합니다.
<type>(optional scope): <subject>
📚 커밋 메시지 <type> 종류
| 타입 | 설명 |
|---|---|
feat | 새로운 기능 추가 |
fix | 버그 수정 |
docs | 문서 수정 (README 등) |
style | 코드 포맷팅, 세미콜론 누락 등 (기능 변경 X) |
refactor | 코드 리팩토링 (기능 변경 X) |
test | 테스트 코드 추가/수정 |
chore | 빌드 시스템, 패키지 매니저 설정 변경 등 |
perf | 성능 개선 |
ci | CI 설정 변경 (예: GitHub Actions) |
build | 빌드 관련 변경 (예: webpack 설정 수정) |
revert | 이전 커밋 되돌리기 |
🔍 (optional scope) → scope란?
feat(auth), fix(cart)📝 subject 작성 규칙
Add, Fix → add, fix)
<body>,<footer>
💡 body와 footer(선택 사항)
body: 변경 이유나 이전 동작 대비 변화 설명footer:BREAKING CHANGE → 하위 호환 깨질 때Closes #123 → 관련 이슈 자동 닫기✅ 실제 예시 모음
feat(auth): 소셜 로그인 기능 추가
사용자는 구글 계정을 통해 로그인할 수 있습니다.
Firebase Authentication API를 사용했습니다.
closes #102
fix(ui): 버튼 클릭 시 페이지 이동 안 되는 오류 수정
이벤트 위임 방식 문제로 인해 클릭이 무시되었습니다.
이벤트 리스너 위치를 상위 컴포넌트로 이동했습니다.
관련 이슈: #88
docs(readme): 설치 및 실행 방법 추가
개발 환경 구성 방법, `.env` 설정 방법 등을 추가했습니다.
chore(legacy): 오래된 로그인 페이지 제거
`/legacy-login` 라우트 및 관련 스타일, API 제거
closes #120
📌 관련 도구
| 도구 | 설명 |
|---|---|
| Commitizen | 커밋 메시지를 CLI로 가이드해 주는 도구 |
| standard-version | 커밋 메시지 기반 자동 릴리즈 도구 |
| semantic-release | Semantic Versioning을 자동화하는 툴 |
1. 기본 구조 생성
App.java 클래스 생성public class App {
public static void main(String[] args) {
System.out.println("계산기 앱 시작");
// 메뉴 구성 예정
}
}
feat: create CalculatorApp main structure
2. 연산자 Enum 분리
Operator.java enum 생성public enum Operator {
ADD, SUB, MUL, DIV;
public static Operator from(String symbol) {
return switch (symbol) {
case "+" -> ADD;
case "-" -> SUB;
case "*" -> MUL;
case "/" -> DIV;
default -> throw new IllegalArgumentException("지원하지 않는 연산자입니다.");
};
}
}
feat(operator): define enum for basic operators
3. 계산 로직 분리
Calculator.java 클래스 생성calculate(int a, int b, Operator op) 메서드 구현public class Calculator {
public static int calculate(int a, int b, Operator op) {
return switch (op) {
case ADD -> a + b;
case SUB -> a - b;
case MUL -> a * b;
case DIV -> a / b;
};
}
}
feat(calculator): implement basic calculate method with switch expression
4. 예외 처리 추가
InvalidOperatorException.java 생성public class InvalidOperatorException extends Exception {
public InvalidOperatorException(String message) {
super(message);
}
}
// Calculator.java 수정
case DIV -> {
if (b == 0) throw new InvalidOperatorException("0으로 나눌 수 없습니다.");
yield a / b;
}
fix(calculator): handle division by zero with custom exception
5. 사용자 입력 처리
Scanner 사용하여 입력받기public class App {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("첫 번째 숫자: ");
int a = sc.nextInt();
System.out.print("두 번째 숫자: ");
int b = sc.nextInt();
System.out.print("연산자 (+, -, *, /): ");
String opSymbol = sc.next();
try {
Operator op = Operator.from(opSymbol);
int result = Calculator.calculate(a, b, op);
System.out.println("연산 결과: " + result);
} catch (Exception e) {
System.out.println("! 에러 발생: " + e.getMessage());
}
}
}
feat(app): complete user input and output for calculator app
- 최종 커밋 로그

> 전체 코드
- Operator.java
public enum Operator {
ADD, SUB, MUL, DIV;
public static Operator from(String symbol) {
return switch (symbol) {
case "+" -> ADD;
case "-" -> SUB;
case "*" -> MUL;
case "/" -> DIV;
default -> throw new IllegalArgumentException("지원하지 않는 연산자입니다.");
};
}
}
- InvalidOperatorException.java
public class InvalidOperatorException extends Exception {
public InvalidOperatorException(String message) {
super(message);
}
}
- Calculator.java
public class Calculator {
public static int calculate(int a, int b, Operator op) throws InvalidOperatorException {
return switch (op) {
case ADD -> a + b;
case SUB -> a - b;
case MUL -> a * b;
case DIV -> {
if (b == 0) throw new InvalidOperatorException("0으로 나눌 수 없습니다.");
yield a / b;
}
};
}
}
- App.java
public class App {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("첫 번째 숫자: ");
int a = sc.nextInt();
System.out.print("두 번째 숫자: ");
int b = sc.nextInt();
System.out.print("연산자 (+, -, *, /): ");
String opSymbol = sc.next();
try {
Operator op = Operator.from(opSymbol);
int result = Calculator.calculate(a, b, op);
System.out.println("연산 결과: " + result);
} catch (Exception e) {
System.out.println("! 에러 발생: " + e.getMessage());
}
}
}