TroubleShooting

[TroubleShooting] 백엔드 트러블슈팅

teddy bear 2026. 7. 5. 20:01
반응형

common-api — 이슈·트러블슈팅 내역

대상: 인증·권한(lx.svcauth), UserForge(lx.gygo.userdataset), 공통 인프라

 


목차

  1. 빌드·의존성
  2. Redis·캐시
  3. 보안약점 진단 대응
  4. 런타임 오류·API 응답
  5. 인증·권한(Auth)
  6. UserForge(사용자 맞춤 데이터셋)
  7. 로컬 개발 체크리스트
  8. 알려진 제약·후속 과제

1. 빌드·의존성

1-1. Nexus 의존성 다운로드 실패

항목내용

증상 ./gradlew compileJava 또는 bootRun 시 Maven 아티팩트 resolve 실패
원인 사내 Nexus(build.gradle의 192.168.211.22:8081) 접근 불가 — VPN·사내망 미연결
조치 VPN 연결 후 재시도. URL 변경 이력 있음(5ea8536 — MAVEN Nexus 주소 변경)
예방 CI·로컬 모두 Nexus 접근 가능 환경에서 빌드
./gradlew compileJava --refresh-dependencies

1-2. SSO JAR 누락으로 빌드 실패

항목내용

증상 exlibs/WAJavaAPI_*.jar, exlibs/lxkeyiden_*.jar 관련 compile error
원인 build.gradle이 implementation files('exlibs/...') 로 로컬 JAR 참조
조치 저장소에 exlibs/ 디렉터리·JAR 존재 확인. 없으면 팀에서 JAR 수령 후 배치

1-3. 보안약점 보완 후 컴파일 오류

항목내용

증상 Code-Ray/보안 보완 커밋 이후 AuthServiceImpl 등에서 컴파일 에러
대표 커밋 c4698fc(66p까지 빌드 오류 해결), 8f37a79(빌드에러 해결)
원인 예 null 체크 추가 시 주석 처리된 for-loop 변수(lxMemberEach)를 참조하는 코드 잔존
조치 보완 PR 머지 후 ./gradlew compileJava 로 즉시 검증. 주석·리팩터링 잔여 코드 정리

1-4. SpotBugs 경고

항목내용

설정 build.gradle — ignoreFailures = true, config/spotbugs/exclude.xml
제외 패턴 DTO EI_EXPOSE_REP, Spring @Service 생성자 주입 EI_EXPOSE_REP2, auth domain 필드 등
참고 NP_*(null pointer) 패턴은 별도 Code-Ray 진단과 SpotBugs를 함께 참고

2. Redis·캐시

2-1. Redis 연결 실패 (운영·개발 환경)

항목내용

증상 기동 시 Redis connection refused, 캐시·토큰 관련 기능 오동작
원인 인프라 IP 변경 후 application-*.yml 미반영. dev/prod Redis host·password 불일치
조치 커밋 291e793(레디스 문제) — dev/prod Redis host를 192.168.211.x 대역으로, password authredis 로 정합
로컬 spring.redis.host: localhost, 비밀번호 없음 — docker compose up -d 로 Redis 기동
# 프로젝트 루트
docker compose up -d
redis-cli ping   # PONG 확인

2-2. 로컬 Redis 미기동

항목내용

증상 local 프로필에서 Spring Boot 기동 실패 또는 cache/redis bean error
조치 README 4절 — Redis 6379 포트로 기동. 포트 변경 시 application-local.yml의 spring.redis 수정

3. 보안약점 진단 대응

행정안전부 SW 보안약점 진단(Code-Ray 등) 대응이 2026년 4~6월에 집중 반영되었습니다. 관련 커밋: 0633322 ~ 60a1dac (40p → 159p 구간).

3-1. CWE-476 — Null Pointer 역참조

항목내용

위험도 중간
증상 정적 분석 도구가 xxx.trim(), list.get(0), Map 역참조 등 지적
대응 NullSafeStrings, NullSafeCollections, MapUtil null-safe 메서드 도입·확산
적용 범위 AuthServiceImpl, UserDatasetService, Shapefile import/export 서비스 등
참고 문서 docs/null-pointer.md

패턴 예시 (Before → After):

// Before — trim() null 역참조 가능
String id = rawId.trim();

// After
String id = NullSafeStrings.trimOrEmpty(rawId);
// Before — MyBatis 결과 list null 가능
for (UserDatasetRow row : rows) { ... }

// After
for (UserDatasetRow row : NullSafeCollections.listOrEmpty(rows)) { ... }

Revert 이력: 66e1484 Revert "CWE-476" — 일부 변경 롤백 후 bc4d28e에서 재보완. 대규모 null-safe 리팩터링 시 기능 회귀·빌드 오류를 ./gradlew test compileJava로 확인할 것.

3-2. CWE-489 — Leftover Debug Code (오탐)

항목내용

지적 대상 SvcAuthApplication.main(String[] args)
판단 오탐(False Positive) — Spring Boot 표준 진입점, 디버그 출력·백도어 없음
조치 클래스 Javadoc에 오탐 근거 명시. 삭제 시 JAR 기동 불가
참고 문서 docs/security-finding-mainmethod-falsepositive.md

3-3. CWE-390 — Unchecked Error Condition

항목내용

대응 UserDatasetShapefileDbfExportService 등 — 예외·리턴값 검증 보강 (95ade98)
요지 DBF/파일 I/O 실패를 무시하지 않고 명시적 처리

3-4. CWE-754 — Improper Check for Unusual or Exceptional Conditions

항목내용

대응 UserDatasetService — catch (Exception) 과다 포괄을 IOException, DataAccessException 등으로 한정
추가 runCommonDao() 래퍼로 DAO 호출 예외를 DataAccessResourceFailureException으로 변환
Shapefile InvalidPathException, DBFException 등 구체 예외 분기

3-5. debug 구문·미사용 로그 정리

항목내용

커밋 01fb72b — debug 구문이 사용되지 않도록 수정
범위 UserDatasetService, Shapefile import/export, JwtRequestFilter 등
조치 System.out, 불필요 log.debug 제거 — 진단 도구의 “디버그 코드” 항목 재지적 방지

4. 런타임 오류·API 응답

4-1. 공통 에러 응답 구조

대부분의 API는 HTTP 200 + 본문 resCode/errCode로 실패를 전달합니다.

{
  "reqId": "...",
  "resCode": "P001",
  "resData": {},
  "errCode": "E005",
  "errMsg": "인증 되지 않았습니다 | ..."
}

errCode의미흔한 원인

E001 요청값 오류 필수 필드 누락, Bean Validation 실패
E005 미인증 JWT·X-User-Id 없음
E006 미인가 타 사용자 리소스 접근
E009 데이터 없음 존재하지 않는 datstId/rcrdId
E012 reqId 없음 /auth-service/auth에 잘못된 reqId
E014~`E018` JWT 오류 만료, 형식, 서명, 로그아웃 토큰
E031 NPE 내부 null 참조 — NullPointerException → AbstractRestHandler에서 매핑

4-2. E031 (NullPointerException) 운영 대응

항목내용

증상 errCode: E031, 메시지 “필수 값 누락 또는 null 참조…”
원인 요청 body null, DB/MyBatis 결과 null, 파일 메타 리스트에 null 항목
대응 커밋 878e4a6, 8c9500c, d103bd0
조치 컨트롤러에서 Objects.requireNonNull(req), 서비스에서 NullSafeCollections·null skip loop

로컬 디버깅: application-*.yml의 api.debug-expose-exception: true(기본)이면 E000 등에 예외 클래스·메시지가 포함됩니다. 운영에서는 false 권장.

4-3. JSON 파싱 오류 (E000)

항목내용

증상 HttpMessageNotReadableException → “잘못된 JSON 형태입니다…”
원인 Content-Type 미지정, 키 따옴표 누락, trailing comma
조치 Content-Type: application/json, Swagger 예시 본문 그대로 전송

4-4. multipart 업로드 실패

항목내용

증상 대용량 ZIP 업로드 시 413 또는 connection reset
설정 spring.servlet.multipart.max-file-size: 64MB, max-request-size: 1024MB, Tomcat max-http-form-post-size: 1024MB
조치 프록시(Nginx/API Gateway) 별도 body size limit 확인. Gateway limit < 앱 limit 이면 Gateway에서 먼저 차단됨

5. 인증·권한(Auth)

5-1. 로그인·토큰 (AUTH_REQ001~003)

증상가능 원인확인

E010 사용자 없음 DB 사용자 미등록, userIden 불일치 reqData의 로그인 ID
E007 비밀번호 불일치 평문·해시 정책 불일치 AUTH_REQ001 요청 본문
E014 토큰 만료 access token TTL 경과 AUTH_REQ003으로 재발급
E016 로그아웃 토큰 이미 로그아웃 처리된 access token 재로그인
E017 refresh 없음 Redis/DB에 refresh token 없음 Redis 연결·세션 저장 확인

5-2. SSO (AUTH_REQ004~005)

증상errCode조치

ssoToken 없음 E022 WeLand 리다이렉트 파라미터 확인
keyValue 없음 E019 지자체 Key SSO keyValueB64Encode 전달 확인
복호화 실패 E021/E024 exlibs JAR·RSA 키 설정(ssoweland, key SSO 키)
keyValue 재사용 E028 동일 암호문 재전송 — 새로 발급 필요

5-3. JWT 필터·헤더

JwtRequestFilter는 다음 순서로 토큰 추출:

  1. Authorization: Bearer ...
  2. 커스텀 access token 헤더
  3. 쿼리/쿠키 파라미터 파싱

토큰 검증 실패 시 SecurityContext 미설정 → 이후 API는 E005.

개발/테스트: Postman 등에서 X-User-Id: {loginId} 헤더만으로 UserForge API 호출 가능(JWT 없이).

5-4. 공통코드 서비스 연동

항목내용

증상 인증·메뉴 API에서 외부 코드 조회 실패
조치 291e793 — commoncodeservice.host를 http://192.168.211.52:8000 으로 변경
확인 해당 호스트 reachable, /code-service/getCdAllByCdLclas.do 응답

6. UserForge(사용자 맞춤 데이터셋)

6-1. 파일 저장 경로(NAS) 오류 — E004

항목내용

증상 데이터셋 등록·ZIP 저장 시 FileStorageException → E004
원인 userforge.storage.path 디렉터리 없음, Windows/Linux 경로 불일치, NAS 마운트 실패
조치 application-local.yml / application-lxdev.yml / application-lxprod.yml 에 환경별 path 설정 (8260920 — lxdev/lxprod NAS 경로 추가)
로컬 D:/... 등 본인 PC에 디렉터리 생성 후 경로 수정 (README 7절)

저장 구조: {basePath}/user-datasets/{userId}/{datasetId}/{filename}.zip

6-2. Shapefile ZIP 업로드·미리보기

증상원인조치

미리보기 geometry 없음 ZIP 내 .shp 없음, 좌표계 미지원 ZIP 구조·CRS 확인
DBF 속성 깨짐 인코딩(EUC-KR/CP949) ShapefileDbfAttributesService charset 처리
레이어 다수 SHP 단일 shp만 가정한 초기 버전 67208e6 — 다중 SHP 속성 CRUD 보완

2단계 저장 플로우:

  1. POST .../shp-geometry/preview — DB 저장 전 geometry 확인
  2. POST .../{datstId}/main-zip — 본 저장

미리보기 없이 본 저장만 호출하면 geometry·속성 불일치 가능.

6-3. SHP 재등록·ROW DATA 불러오기

항목내용

증상 기존 데이터셋에 SHP 재업로드 후 속성·original cells 불일치
커밋 19b5b2b — SHP 파일 재등록 및 ROW DATA 불러오기 기능 보완
추가 API GET .../records/{rcrdId}/original-cells — 초기화 미리보기
조치 reset 플래그·shpRelativePath(다중 레이어) 파라미터 확인

6-4. 컬럼(필드) 추가·삭제·이름 변경

항목내용

증상 PUT .../update/{datstId} 후 필드 순서·이름·삭제 반영 안 됨
커밋 c1090fb, b65a794, 67208e6
조치 UserDatasetUpdateReq.fields, attributeCellOverrides 스키마 준수. 컨트롤러에서 @Valid/checkReqValue 통과 확인

6-5. 다중 SHP 레이어·속성 CRUD

항목내용

배경 ZIP 하나에 .shp가 여러 개인 경우 레이어별 geometry·DBF 행 관리 필요
커밋 67208e6 (+2100 lines) — ShpLayerGeometryDto, ShpLayerAttributeRowsDto, export/import 전면 보강
증상 특정 레이어만 수정했는데 전 레이어 덮어쓰기
MyBatis userdatasetfield.xml, userdatasetrow.xml 쿼리 동시 변경 — 배포 시 mapper XML 누락 주의

6-6. 데이터셋 검색·목록

항목내용

커밋 890c76b — 검색 필터링 로직 추가
증상 검색 결과 누락

6-7. MapUtil·selectKey 타입 불일치

항목내용

증상 insert 후 PK 추출 시 ClassCastException 또는 E030
조치 MapUtil.requireLongObject(), requireLongKey() — MyBatis selectKey 반환 타입(Long/Integer) 통일 처리 (d103bd0)

7. 로컬 개발 체크리스트

기동 실패 시 아래 순서로 확인합니다.

#확인 항목명령/위치

1 JDK 15 java -version
2 VPN → Nexus ./gradlew compileJava
3 Redis 6379 docker compose up -d
4 PostgreSQL + PostGIS application-local.yml → spring.datasource.hikari.primary
5 search_path SET search_path TO "GYGO", public (connection-init-sql)
6 exlibs/ JAR SSO JAR 존재
7 userforge.storage.path 디렉터리 생성·쓰기 권한
8 Active profile 기본 local (application.yml)
9 포트 8029 server.port 충돌 없음

기동 후 스모크 테스트:

GET http://localhost:8029/swagger-ui/index.html
GET http://localhost:8029/api/user-datasets/list
X-User-Id: {테스트로그인ID}
POST http://localhost:8029/auth-service/auth
Content-Type: application/json

{ "reqId": "AUTH_REQ001", "reqData": { ... } }

8. 알려진 제약·후속 과제

구분내용

HTTP 상태 코드 전역 핸들러 특성상 다수 API가 실패 시에도 HTTP 200 — 클라이언트는 resCode/errCode로 판단 필요 (api-spec.md 0-1절)
이중 DB primary/secondary Hikari — Gygo 기능은 주로 primary. secondary 용도는 lx.svcauth.config.DB 참고
Spring Boot 2.7 / Java 15 LTS 아님 — 장기적으로 JDK·Boot 업그레이드 검토
WebSocket 샘플 ChatWebSocketConfig — /chat-sample-service/ws, SockJS, setAllowedOriginPatterns("*") (58b41b0 소켓설정). 운영 CORS 정책 별도 검토
Code-Ray 대량 보완 40p→159p 구간 다수 커밋 — 회귀 테스트(인증 로그인, 데이터셋 CRUD, Shapefile 업로드) 권장
민감 설정 DB password, JWT secret, Redis password — 저장소 커밋 지양, 환경 변수·Config Server 주입

부록: 관련 커밋 타임라인 (요약)

시기커밋 메시지 (요지)영역

2026-04 데이터셋 CRUD, SHP 업로드, 검색, NAS 경로 UserForge
2026-04 SHP 재등록·ROW DATA, 컬럼 추가/삭제 UserForge
2026-04 Nexus 주소 변경, Redis/commoncode host 변경 인프라
2026-04~05 CWE-390/476/754, 보안약점 40p~159p 전역
2026-05 다중 SHP 속성 CRUD 대규모 보완 UserForge
2026-06 NPE 처리, NullSafe 유틸, debug 구문 제거, 빌드에러 전역·UserForge·Auth
2026-06 WebSocket 설정 chat-sample

참고 문서

문서설명

README.md 로컬 설치·실행
api-spec.md API 명세·공통 에러 코드
null-pointer.md CWE-476 가이드
security-finding-mainmethod-falsepositive.md CWE-489 오탐 회신
blog-gygo-svc3d-back-common-api.md 프로젝트 소개 블로그

문서 버전: 1.0 · user-loc 도메인 이슈는 본 문서 범위에서 제외했습니다.