From a5d70b055333d5b800fcf764387a47d90a62790a Mon Sep 17 00:00:00 2001 From: htlee Date: Wed, 18 Feb 2026 21:16:42 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix(hook):=20commit-msg=20=EC=A0=95?= =?UTF-8?q?=EA=B7=9C=EC=8B=9D=20=ED=86=B5=EC=9D=BC=20(template-common=20v1?= =?UTF-8?q?.2.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .githooks/commit-msg | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.githooks/commit-msg b/.githooks/commit-msg index 20260d9..67be0a9 100755 --- a/.githooks/commit-msg +++ b/.githooks/commit-msg @@ -20,13 +20,14 @@ fi # Conventional Commits 정규식 # type(scope): subject # - type: feat|fix|docs|style|refactor|test|chore|ci|perf (필수) -# - scope: 영문, 숫자, 한글, 점, 밑줄, 하이픈 허용 (선택) -# - subject: 1~72자, 한/영 혼용 허용 (필수) -PATTERN='^(feat|fix|docs|style|refactor|test|chore|ci|perf)(\([a-zA-Z0-9가-힣._-]+\))?: .{1,72}$' +# - scope: 괄호 제외 모든 문자 허용 — 한/영/숫자/특수문자 (선택) +# - subject: 1자 이상 (길이는 바이트 기반 별도 검증) +PATTERN='^(feat|fix|docs|style|refactor|test|chore|ci|perf)(\([^)]+\))?: .+$' +MAX_SUBJECT_BYTES=200 # UTF-8 한글(3byte) 허용: 72문자 ≈ 최대 216byte FIRST_LINE=$(head -1 "$COMMIT_MSG_FILE") -if ! [[ "$FIRST_LINE" =~ $PATTERN ]]; then +if ! echo "$FIRST_LINE" | grep -qE "$PATTERN"; then echo "" echo "╔══════════════════════════════════════════════════════════════╗" echo "║ 커밋 메시지가 Conventional Commits 형식에 맞지 않습니다 ║" @@ -58,3 +59,13 @@ if ! [[ "$FIRST_LINE" =~ $PATTERN ]]; then echo "" exit 1 fi + +# 길이 검증 (바이트 기반 — UTF-8 한글 허용) +MSG_LEN=$(echo -n "$FIRST_LINE" | wc -c | tr -d ' ') +if [ "$MSG_LEN" -gt "$MAX_SUBJECT_BYTES" ]; then + echo "" + echo " ✗ 커밋 메시지가 너무 깁니다 (${MSG_LEN}바이트, 최대 ${MAX_SUBJECT_BYTES})" + echo " 현재 메시지: $FIRST_LINE" + echo "" + exit 1 +fi From 49a954a1ddc9535a22de561d2a6dde29f619c862 Mon Sep 17 00:00:00 2001 From: htlee Date: Thu, 19 Feb 2026 07:29:12 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix(rules):=20SLF4J=20=EB=A1=9C=EA=B9=85=20?= =?UTF-8?q?=EC=A7=80=EC=B9=A8=20=EC=B6=94=EA=B0=80=20(printf=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=B7=20=EC=82=AC=EC=9A=A9=20=EA=B8=88=EC=A7=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/rules/code-style.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.claude/rules/code-style.md b/.claude/rules/code-style.md index 0cb1563..f5f0203 100644 --- a/.claude/rules/code-style.md +++ b/.claude/rules/code-style.md @@ -44,7 +44,21 @@ - `@Builder` 허용 - `@Data` 사용 금지 (명시적으로 필요한 어노테이션만) - `@AllArgsConstructor` 단독 사용 금지 (`@Builder`와 함께 사용) -- `@Slf4j` 로거 사용 + +## 로깅 +- `@Slf4j` (Lombok) 로거 사용 +- SLF4J `{}` 플레이스홀더에 printf 포맷 사용 금지 (`{:.1f}`, `{:d}`, `{%s}` 등) +- 숫자 포맷이 필요하면 `String.format()`으로 변환 후 전달 + ```java + // 잘못됨 + log.info("처리율: {:.1f}%", rate); + // 올바름 + log.info("처리율: {}%", String.format("%.1f", rate)); + ``` +- 예외 로깅 시 예외 객체는 마지막 인자로 전달 (플레이스홀더 불필요) + ```java + log.error("처리 실패: {}", id, exception); + ``` ## 예외 처리 - 비즈니스 예외는 커스텀 Exception 클래스 정의 From 299d8bd33312030987c8906f2b0cbec60ce15c60 Mon Sep 17 00:00:00 2001 From: htlee Date: Thu, 19 Feb 2026 15:20:27 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix(auth):=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=8B=9C=20=ED=94=84=EB=A1=9D=EC=8B=9C=20=EC=BA=90?= =?UTF-8?q?=EC=8B=9C=20=EC=BF=A0=ED=82=A4=20=EC=82=AD=EC=A0=9C=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=9D=BC=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 삭제 쿠키에 Secure, HttpOnly, SameSite 속성 추가하여 원본 쿠키와 매칭되도록 수정 (gc_proxy_auth, GC_SESSION) Co-Authored-By: Claude Opus 4.6 --- src/main/java/com/gcsc/guide/auth/AuthController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gcsc/guide/auth/AuthController.java b/src/main/java/com/gcsc/guide/auth/AuthController.java index 575fc35..45ee606 100644 --- a/src/main/java/com/gcsc/guide/auth/AuthController.java +++ b/src/main/java/com/gcsc/guide/auth/AuthController.java @@ -227,9 +227,11 @@ public class AuthController { private void clearSessionCookies(HttpServletResponse response) { response.addHeader(HttpHeaders.SET_COOKIE, - ResponseCookie.from("GC_SESSION", "").path("/").maxAge(0).build().toString()); + ResponseCookie.from("GC_SESSION", "") + .path("/").httpOnly(true).secure(true).sameSite("Lax").maxAge(0).build().toString()); response.addHeader(HttpHeaders.SET_COOKIE, - ResponseCookie.from("gc_proxy_auth", "").path("/").maxAge(0).build().toString()); + ResponseCookie.from("gc_proxy_auth", "") + .path("/").httpOnly(true).secure(true).sameSite("Lax").maxAge(0).build().toString()); } private String getCookieValue(HttpServletRequest request, String name) {