Skip to content

Conversation

@can019
Copy link
Collaborator

@can019 can019 commented Sep 26, 2025

📝 작업 내용

  • application.ymlspring.jackson.time-zone 설정이 무시되는 문제 해결
  • ObjectMapper를 Bean으로 등록해 UTC TimeZoneWRITE_DATES_AS_TIMESTAMPS 비활성화 적용
  • WorkflowExecutionLogDto에서 Instant 직렬화 시 Z(UTC) 표기가 누락되는 현상 수정
  • 기존 application.yml 기반 설정 제거 후 Bean 설정으로 일원화
image

🔗 관련 이슈


💬 추가 요청사항

  • 다른 모듈에서도 동일한 Instant 직렬화가 정상적으로 출력되는지 확인 부탁드립니다.

✅ 체크리스트

코드 품질

  • 커밋 컨벤션 준수 (fix)
  • 불필요한 코드/주석 제거

테스트

  • 로컬 환경에서 Jackson 직렬화 결과에 Z(UTC) 포함 확인
  • 기존 기능에 영향 없음 확인

배포 준비

  • 환경변수 추가/변경사항 없음
  • DB 마이그레이션 필요 없음
  • 배포 시 주의사항 없음

@can019 can019 changed the title Fix/timezone Jackson Timezone 직렬화가 되지 않던 문제 해결 Sep 26, 2025
@can019 can019 added the bug Something isn't working label Sep 26, 2025
@can019 can019 requested review from bwnfo3 and jihukimme September 26, 2025 03:58
@can019 can019 marked this pull request as ready for review September 26, 2025 04:00
@jihukimme
Copy link
Member

시간 데이터를 String이 아닌 Instant로 처리해서 시간 관련 기능을 확장할 때 발생할 수 있는 버그를 사전에 방지한 것에 대해서 좋은 리팩토링 이었다고 생각합니다.

시간 데이터를 명확하고 표준화된 방식으로 처리

  • String 타입의 문제점:

    • 모호함: "2025-09-26 15:30:00"과 같은 문자열은 타임존(Timezone) 정보가 없어, 어느 지역 기준의 시간인지 알 수 없습니다.
    • 계산의 어려움: 두 시간의 차이(duration)를 계산하려면 문자열을 날짜/시간 객체로 매번 파싱(Parsing)해야 해서 비효율적이고 오류가 발생하기 쉽습니다.
    • 형식 비일관성: "2025/09/26", "26-09-2025" 등 다양한 형식으로 데이터가 저장될 위험이 있습니다.
  • Instant 타입의 장점:

    • 명확성: Instant는 UTC(협정 세계시)를 기준으로 하는, 시간대의 영향을 받지 않는 절대적인 '순간'을 표현합니다. 전 세계 어디서든 동일한 시점을 의미합니다.
    • 정확한 계산: 시간 차이를 나노초(nanosecond) 단위까지 정밀하게 계산할 수 있습니다.
    • 표준화: API 응답(JSON) 등에서 ISO 8601 형식(2025-09-26T06:30:00Z)으로 직렬화되어, 다른 시스템과 데이터를 주고받을 때 오해의 소지가 없습니다

Copy link
Member

@jihukimme jihukimme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instant 타입과 같은 시간 데이터를 처리하는 타입에 대해서 인지하지 못하고 있었습니다. 해당 PR을 보고 시간 데이터를 처리하는 타입을 잘 처리해주어야 함을 알게 되었습니다.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InstantTimezone을 나누어 사용하는 이유는 데이터의 정확성시스템의 유연성을 모두 확보하기 위함이며, 이는 서버 개발의 표준적인 모범 사례입니다.


Instant vs. Timezone

구분 | Instant (언제) | Timezone (어디서) -- | -- | -- 핵심 개념 | 타임라인 위의 절대적인 한 순간 | 특정 지역의 시간을 정의하는 규칙 또는 오프셋 기준 | 항상 UTC (협정 세계시) | UTC로부터의 시간 차이 (예: +09:00) 표현 예시 | 2025-09-26T07:00:00Z | Asia/Seoul, KST 역할 | 기계가 이해하는 시간 (내부 처리용) | 사람이 이해하는 시간 (표시용)

왜 나누어서 사용해야 하는가?

서버는 전 세계 어디서든 동작할 수 있고, 사용자 또한 전 세계 어디서든 접속할 수 있습니다. 이때 시간 데이터를 모호하게 처리하면 심각한 문제가 발생할 수 있습니다.

1. 데이터 무결성 보장: 서버는 Instant를 사용

서버 내부, 특히 데이터베이스에 시간을 기록할 때는 전 세계적으로 동일한 절대적인 시점을 나타내는 Instant (UTC 기준)를 사용해야 합니다.

  • 장점: 서울에 있는 서버가 기록한 Instant 값과, 나중에 미국에 있는 서버가 그 값을 읽었을 때, 두 서버는 완벽하게 동일한 물리적 순간을 이해하게 됩니다. 만약 "2025년 9월 26일 오후 4시"와 같이 지역 시간으로 저장했다면, 이것이 한국 시간인지 미국 시간인지 알 수 없어 데이터의 정합성이 깨집니다.

2. 유연한 표현: 사용자는 Timezone이 적용된 시간을 봄

사용자에게 시간을 보여줄 때는, 서버에 저장된 절대적인 Instant 값에 사용자의 지역 Timezone을 적용하여 변환한 후 보여줍니다.

  • 장점: 한국에 있는 사용자는 "오후 4시"로 보고, 동시에 접속한 미국 사용자는 "오전 3시"로 보게 됩니다. 이처럼 단 하나의 Instant 데이터만으로 전 세계 모든 사용자에게 각자의 지역 시간에 맞는 올바른 정보를 제공할 수 있습니다.

코드 리뷰 관점에서의 결론

현재 프로젝트에서 TaskRun과 같은 도메인 모델에는 Instant를 사용하여 서버 내부의 데이터 정확성을 확보하고, TaskRunDto와 같은 DTO에서는 이를 String으로 변환하여 외부(클라이언트)에 명확한 정보를 제공하는 방식은, 이러한 역할과 책임을 완벽하게 분리한 매우 훌륭하고 표준적인 설계입니다.

*/
@Bean
@Primary
public ObjectMapper objectMapper() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전역 ObjectMapper를 Bean으로 등록하여 애플리케이션 전체의 JSON 직렬화/역직렬화 방식을 표준화한 것은 매우 훌륭한 설계입니다. 특히 시간(Time) 데이터를 UTC로 통일한 것은 분산 시스템에서 발생할 수 있는 수많은 버그를 사전에 방지하는 핵심적인 모범 사례입니다.

잘된 점 (Pros)

  1. 중앙화된 시간 관리 (setTimeZone):

    • .setTimeZone(TimeZone.getTimeZone("UTC")) 설정을 통해, 애플리케이션의 모든 JSON 변환에서 시간을 UTC(협정 세계시) 기준으로 통일했습니다. 이는 서버의 위치나 설정에 관계없이 항상 일관된 시간 데이터를 보장하여, 타임존 관련 버그를 원천적으로 차단하는 가장 좋은 방법입니다.
  2. Java 8 시간 API 지원 (JavaTimeModule):

    • .registerModule(new JavaTimeModule())을 통해 Instant, LocalDateTime 등 Java 8의 시간 타입을 Jackson이 올바르게 처리할 수 있도록 했습니다. 이는 최신 Java 개발 환경의 필수 설정입니다.
  3. 가독성 높은 시간 포맷 (WRITE_DATES_AS_TIMESTAMPS):

    • .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) 설정을 통해, 1727339859000과 같은 숫자 타임스탬프 대신, 사람이 읽기 쉬운 ISO 8601 형식(2025-09-26T09:17:39.000+00:00)의 문자열로 시간을 표현하도록 했습니다. 이는 API 응답을 디버깅하고 이해하는 데 큰 도움이 됩니다.
  4. 전역 적용 (@Primary):

    • @Primary 어노테이션을 사용하여, 여기서 설정한 ObjectMapper가 Spring 컨테이너 내에서 기본값으로 사용되도록 했습니다. 이제 @RestController가 JSON 응답을 생성할 때 등 별도의 설정 없이도 이 Bean이 자동으로 사용되어 애플리케이션 전체의 동작 일관성을 보장합니다.

추가 제안 (고려 사항)

  • API의 안정성을 더욱 높이고 싶다면, 역직렬화(JSON -> Java Object) 시 알 수 없는 필드가 있어도 에러를 발생시키지 않도록 아래 설정을 추가하는 것을 고려해볼 수 있습니다.
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

결론적으로, 이 코드는 안정적이고 예측 가능한 데이터 처리를 위한 훌륭한 기반을 마련한 매우 잘 작성된 설정입니다.

@can019 can019 merged commit 9e4aa31 into develop Sep 26, 2025
8 checks passed
@can019 can019 deleted the fix/timezone branch September 26, 2025 11:03
can019 added a commit that referenced this pull request Sep 27, 2025
* 워크플로우 default_config 사용 로직개발 (#190)

* :fix
- 스케줄 스키마 유니크 제약조건 제거
- workflow insert문 default_config 로직에 맞게 수정

* refactor: 워크 플로우 하드코딩 제거
1. workflow 테이블에서 default_config값 가져오기
2. 각 task_id에맞게 config값이 setting에 들어가도록 수정
3. 각각의 taskBuilder에 하드코딩된값 변경

* fix: 코드 머지후 다시 코드 작성

* style: springBoot 코드 포맷팅

* fix:v0.0.5 Alter 스키마 삭제

* check-session, permissons api에 대한 테스트 코드 작성 (#200)

* feat: S3 하고 RDB 연동
1. body builder 추가
2. s3_upload + 임시 1개 뽑기 -> rag_create 를 s3_upload + rdb 데이터 삽입 -> 임시 1개 뽑기 -> rag_create로 변경
3. 변경에 따라 schemas 수정

* chore: poetry run black . & spotlessApply

* refactor: 크롤링 서비스 순차적 크롤링에서 비동기 크롤링으로 변경

* refactor: RDB와 selection task 실행시 task_run_id가 mismatch 되는 문제 해결

* chore: poetry run black .

* chore: spotlessApply

* Workflow 수동 실행 및 Retry 로직 테스트 코드 작성 (#209)

* refactor: integration log4j2 설정파일 분리

* refactor: RetryConfig 구성

* feat: Workflow 수동 실행 api 테스트 코드 작성

* feat: task 실패 시 재시도 동작 테스트 코드 작성

* refactor: ApplicationListener로 변경

* refactor: Code Formatting

* refactor: javadoc 수정

* Workflow 생성 api (#211)

* feat: WorkflowCreateDto 초안

* feat: Workflow생성 관련 메서드, sql 추가

* feat: Workflow생성 임시 post api

* feat: WorkflowCreateE2eTest 초안

* feat: WorkflowController 워크플로우 생성 api

* feat: description @null 제거

* feat: 워크플로우 생성시 job,task 생성 메서드 추가

* feat: 워크플로우 생성시 job,task 생성 메서드 추가

* feat: 워크플로우 생성시 job,task 생성 메서드 구현중

* feat: 워크플로우 생성 e2eTest 작성중

* chore: spotlessApply

* feat: job,task 생성 없이 워크플로우 생성으로만 수정

* Timezone Instant(UTC) 마이그레이션 (#210)

* refactor: UTC 기반으로 변경

* refactor: data, schema sql 분리

* fix: IntegrationTest fix

* test: E2e, integration test UTC 검증

* Jackson Timezone 직렬화가 되지 않던 문제 해결 (#212)

* fix: ObjectMapper timezone 설정

* fix: Workflow 관련 timezone이 찍히지 않는 문제

* fix: ObjectMapper UTC 설정 bean config로 변경

application.yml 설정이 무시됨

* fix: ExecutionLogDto instant로 변환

* test: DB가 UTC 기준으로 저장되어있다고 가정하도록 수정

* test: SQL 09:18 -> 18:18로 일치

* test: H2 DB UTC 고정

* ExecutionLog API 구현 및 traceId 일관성 개선 (#215)

* feat: Execution log api

* fix: Workflow run 시 trace id가 달라지던 버그

MDC에서 trace id를 가져오도록 설정
없다면 uuid 재발급

* User 관련 api test 및 api document 작성 (#217)

* test: User 개인 정보 조회 api

* fix: Check email reqeust

Mapper에서 user가 아닌 users로 table 이름을 사용했던 버그
`@NoArgsConstructor`를 통해 직렬화가 실패하던 버그

* test: User check email api

* Gradle 캐싱을 통해 CI (Java) 속도 개선 (#218)

* refactor: ci-java에 gradle action 추가

* feat: gradle properties에서 build caching 활성화

 - 각 Job 내에서 변경되지 않은 task는 UP-TO-DATE 처리
 - Job 간 중복 컴파일은 그대로 두되, Job 내 불필요한 재작업 방지
 - 안전하고 검증된 기본 최적화

* refactor: Test, document step 분리

* chore: 쓸모없는 dev container 삭제

* refactor: lint, test  step 수행 조건 평가 후 실행

각 step이 자신에게 관게있는 파일 변경 시에만 실행

* chore: 변경점에 따라 step 실행 revert

PR sync시에도 base와 변경점을 감지
  - 소스 변경: 모든 테스트 영향 (컴파일 필요)
  - 테스트 변경: 빌드는 필요
  - 의존성 복잡: Unit ↔ Integration 경계 모호

  - 복잡성 증가 > 성능 향상
  - 조건문 길어짐: 가독성 저하
  - 멀티 모듈이라면 의미가 있지만 단일 모듈, 의미 없다고 판단

* refactor: Tag 발행 시 cache가 쓰이도록 변경

* chore: version 0.1.0-SNAPSHOT으로 build.gradle update

* chore: Document artifcat step 분리

* fix: Working directory document-java step에 추가

---------

Co-authored-by: 김경민 <[email protected]>
Co-authored-by: bwnfo3 <[email protected]>
Co-authored-by: thkim7 <[email protected]>
Co-authored-by: Jihu Kim <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants