Pangea
⏎
자동 분석 · 1달 전
로그인
데모 · 1D1S 프로젝트를 읽기 전용으로 보고 있습니다. 편집·AI 실행은 할 수 없습니다.내 레포로 시작하기 →
1D1S
홈브리핑진전이슈8일정화면API지도 탐색플로우문서변경

문서
루트/AI 역문서/1. 개요

AI 초안 · 검토 전노근 · 1달 전 수정

1.3 시스템 아키텍처

AI 역문서 · 1D1S-client@ad5edf9 · 1D1S-admin@5dd0cfe · 1D1S-server-v2@23a5ca8 기준 · 코드에서 거꾸로 만든 초안입니다. 근거 링크로 확인하세요.

관련 소스 파일

  • 1D1S-client/src/app.module/api/client.ts
  • 1D1S-client/src/app.module/api/request.ts
  • 1D1S-client/src/app.feature/chat/socket/chatSocket.ts
  • 1D1S-admin/src/app.module/api/client.ts
  • 1D1S-server-v2/src/main/java/com/odos/odos_server_v2/config/AsyncConfig.java
  • 1D1S-server-v2/src/main/java/com/odos/odos_server_v2/config/FirebaseConfig.java
  • 1D1S-server-v2/src/main/java/com/odos/odos_server_v2/config/JacksonConfig.java
  • 1D1S-server-v2/src/main/java/com/odos/odos_server_v2/config/LoggingFilter.java
  • 1D1S-server-v2/src/main/java/com/odos/odos_server_v2/config/PasswordEncoderConfig.java
  • 1D1S-server-v2/src/main/java/com/odos/odos_server_v2/config/S3Config.java
  • 1D1S-server-v2/src/main/java/com/odos/odos_server_v2/config/SecurityConfig.java
  • 1D1S-server-v2/src/main/java/com/odos/odos_server_v2/config/SwaggerConfig.java
목적과 범위

이 페이지는 1D1S-client, 1D1S-admin, 1D1S-server-v2 사이의 런타임 통신 경계를 설명합니다. 사용자 클라이언트와 관리자 웹은 서버를 직접 데이터 저장소로 사용하지 않고, API_BASE_URL을 기준으로 서버의 HTTP API를 호출하는 구조입니다. 사용자 클라이언트의 일반 요청은 Axios 기반 apiClient, publicApiClient, silentAuthClient를 통해 전송하며, 관리자 웹도 별도의 Axios 클라이언트를 통해 같은 서버 계층에 접근합니다.

채팅은 일반 HTTP 요청과 다른 경로를 사용합니다. 클라이언트는 /ws/chat에 STOMP WebSocket 연결을 만들고 방별 토픽을 구독하지만, 메시지 전송 자체는 POST /chat/rooms/{id}/messages REST 요청으로 수행합니다. 서버는 WebSocket 핸드셰이크와 구독 권한을 보안 필터 및 채팅 인증 인터셉터로 처리하는 것으로 클라이언트 코드에 기록되어 있습니다.

이 페이지에서는 확인 가능한 클라이언트 통신 계층, 서버 보안·CORS·외부 서비스 설정, 채팅 연결과 오류 재시도 경계를 다룹니다. 개별 화면의 기능별 API 계약, 상세 엔티티·리포지터리 구조, 인증 시나리오, 이미지 업로드 상세, 채팅 도메인의 실제 서비스와 저장 로직은 다음 페이지에서 다룹니다.

  • 1.1 서비스와 레포 개요
  • 1.2 저장소 구조
  • 2.5 채팅과 신고
  • 3.1 인증과 세션
  • 3.2 API와 응답 구조
  • 3.3 도메인 데이터 모델
  • 3.4 이미지 업로드
  • 4.1 클라이언트 화면 구조
  • 4.2 관리자 콘솔
  • 5.1 서버 도메인 계층

출처: client.ts:7-31, chatSocket.ts:13-35, client.ts:13-19

구성 요소와 통신 경계

시스템의 확인 가능한 구성 요소와 책임은 다음과 같습니다.

구성 요소코드상 역할서버와 연결되는 방식
1D1S-client의 apiClient인증이 필요한 일반 HTTP 요청을 수행합니다.API_BASE_URL을 baseURL로 사용하는 Axios 요청입니다.
1D1S-client의 publicApiClienthandleUnauthorized: false 옵션으로 생성된 클라이언트입니다.같은 API_BASE_URL을 사용하며 인증 실패를 일반 인증 클라이언트와 다르게 처리합니다.
1D1S-client의 tokenClientauth/token, auth/logout 같은 직접 인증 호출에 사용되는 Axios 인스턴스입니다.쿠키를 포함한 HTTP 요청을 보냅니다.
1D1S-client의 silentAuthClient선택적 인증 요청에서 인증 만료를 조용히 갱신하고 한 번 재시도합니다.401 또는 302 응답 뒤 refreshAccessTokenOnce()를 호출합니다.
1D1S-client의 chatSocketSTOMP 클라이언트 하나를 만들고 방별 토픽을 관리합니다.API_BASE_URL의 프로토콜을 ws: 또는 wss:로 바꾸고 경로를 /ws/chat으로 설정합니다.
1D1S-admin의 apiClient관리자 웹의 인증 HTTP 요청을 수행합니다.API_BASE_URL과 쿠키를 사용하는 Axios 요청입니다.
1D1S-server-v2의 SecurityConfigCORS, 인증 진입점, JWT 필터, 경로별 접근 제어를 구성합니다.HTTP 요청과 /ws/chat 핸드셰이크 요청의 보안 진입점입니다.
S3Client·S3Presigner서버에서 S3를 사용할 수 있는 클라이언트 빈입니다.기본 S3, 채팅 전용 S3, presigned URL 생성 컴포넌트로 분리되어 있습니다.
FirebaseMessaging·pushExecutorFirebase가 설정된 경우 FCM 발송을 위한 외부 연동 구성입니다.사용자 요청 스레드와 분리된 pushExecutor에서 비동기 실행할 수 있습니다.

서버의 컨트롤러가 어떤 도메인 서비스와 리포지터리를 호출하는지는 제공된 원문에 없습니다. 따라서 이 페이지에서는 요청이 SecurityConfig를 통과한 뒤 어떤 실제 도메인 클래스와 데이터베이스 테이블로 이어지는지는 확정하지 않습니다. 확인 가능한 범위에서는 서버가 HTTP 보안 경계, WebSocket 접근 경계, S3·Firebase 외부 서비스 경계를 제공한다고 기술합니다.

출처: client.ts:7-35, client.ts:13-69, SecurityConfig.java:51-134, S3Config.java:11-47

HTTP 요청 흐름
사용자 클라이언트의 일반 요청

클라이언트의 API 호출은 요청을 만드는 Axios 계층과 응답 본문을 추출하는 요청 유틸리티로 나뉩니다.

  1. 화면 또는 기능 모듈이 apiClient, publicApiClient, silentAuthClient 중 하나를 선택합니다.
  2. 선택한 Axios 인스턴스가 API_BASE_URL을 기준으로 상대 경로를 결합합니다. 기본 타임아웃은 DEFAULT_TIMEOUT_MS이며, withCredentials: true이므로 브라우저 쿠키가 요청에 포함됩니다.
  3. 목록·검색 요청이 쿼리 파라미터를 사용하면 buildQueryString()이 URLSearchParams를 만들고, 배열 값은 같은 키를 여러 번 추가합니다.
  4. requestData()가 요청을 실행하고 서버의 ApiResponse<TData>에서 response.data.data만 반환합니다. 따라서 이 함수의 호출자는 공통 응답 래퍼 전체가 아니라 데이터 부분을 받습니다.
  5. 응답이 401 또는 302이고 재시도하지 않은 요청이면 인증 인터셉터가 refreshAccessTokenOnce()를 실행한 뒤 원래 요청을 한 번 다시 보냅니다.
  6. 토큰 재발급이 실패하거나 재시도 후에도 실패하면 Axios 오류가 호출부로 전달됩니다. silentAuthClient는 재발급 실패를 내부에서 삼키지 않고 최종적으로 Promise.reject(error)를 반환합니다.

requestBody()는 ApiResponse 래퍼를 벗기지 않고 응답 본문 전체를 반환하는 별도 경로입니다. 따라서 API별 응답 형식이 requestData()의 공통 응답 형식과 같은지 여부는 각 API 코드에서 확인해야 합니다.

관리자 웹의 일반 요청

관리자 웹도 API_BASE_URL, withCredentials: true, maxRedirects: 0을 사용합니다. apiClient와 silentAuthClient는 401 또는 302를 받으면 refreshAccessTokenOnce() 후 한 번 재요청하고, publicApiClient는 인증 인터셉터 없이 생성됩니다. 인증 갱신 대상에서 /auth/token, /auth/logout, /login/oauth2/code/, /oauth2/authorization/은 shouldSkipRefresh()로 제외됩니다.

제공된 자료에는 관리자 화면이 호출하는 구체적인 도메인 엔드포인트가 없습니다. 따라서 "/admin/**" 경로가 관리자 권한을 요구한다는 서버 규칙은 확인할 수 있지만, 어느 관리자 화면이 어느 API를 호출하는지는 4.2 관리자 콘솔에서 별도 확인해야 합니다.

출처: request.ts:7-59, client.ts:7-24, client.ts:33-68, client.ts:13-69

서버의 HTTP 보안 및 접근 제어

서버는 SecurityConfig.filterChain()에서 HTTP 요청의 공통 보안 정책을 구성합니다. csrf, HTTP Basic, form login, Spring Security logout은 비활성화되어 있고, 세션 생성 정책은 SessionCreationPolicy.STATELESS입니다. 인증은 서버 세션에 의존하는 방식이 아니라 JwtAuthenticationFilter를 포함한 필터 체인을 거치는 구조로 설정되어 있습니다.

경로별 접근 제어는 다음과 같습니다.

경로 또는 조건접근 정책
GET /diaries/**permitAll입니다.
GET /challenges/**permitAll입니다.
GET /comments/**permitAll입니다.
GET /banners, GET /notices/**, GET /guides, GET /guides/**permitAll입니다.
GET /app/versionpermitAll입니다.
/ws/chat, /ws/chat/**핸드셰이크 경로가 permitAll입니다.
/auth/**, /oauth2/**, /login/oauth2/**인증 관련 공개 경로입니다.
/actuator/prometheus, /actuator/health인프라 확인용 공개 경로입니다.
GET /push/health공개 경로입니다.
/admin/**hasRole("ADMIN")이 필요합니다.
그 밖의 요청인증이 필요합니다.

미인증 요청의 진입점은 HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)입니다. 즉 서버는 인증이 필요한 API에서 OAuth 로그인 페이지로 자동 리다이렉트하지 않고 401을 반환하도록 구성되어 있습니다. 클라이언트와 관리자 웹이 401 및 302를 모두 토큰 재발급 대상으로 감지하는 것은 클라이언트 코드에 남아 있지만, 현재 SecurityConfig의 미인증 진입점은 401입니다.

필터 순서에는 LoggingFilter, JwtAuthenticationFilter, CookieMutationOriginFilter, JwtTokenExceptionFilter가 포함됩니다. LoggingFilter는 /actuator와 OPTIONS를 제외하고 요청 메서드·URI·응답 상태·처리 시간을 기록합니다.

출처: SecurityConfig.java:51-134, LoggingFilter.java:12-39

WebSocket 채팅 흐름

채팅은 연결과 메시지 전송을 분리합니다. chatSocket.ts는 탭 전체에서 하나의 Client 인스턴스를 공유하고, 화면은 subscribeChatRoom()으로 방별 리스너를 등록합니다. 마지막 리스너가 제거되어도 연결 자체는 유지하며, 로그아웃 등 명시적인 종료 시에는 closeChatSocket()이 연결을 비활성화합니다.

방 구독과 메시지 처리 흐름은 다음과 같습니다.

  1. 채팅 화면이 subscribeChatRoom(roomId, handlers)를 호출합니다.
  2. ensureClient()가 아직 클라이언트가 없으면 STOMP Client를 생성합니다. API_BASE_URL의 http:·https:를 각각 ws:·wss:로 바꾸고 경로를 /ws/chat으로 설정합니다.
  3. 브라우저 쿠키가 WebSocket 핸드셰이크에 포함됩니다. 주석에 따르면 서버의 JwtAuthenticationFilter가 핸드셰이크 HTTP 요청에서 쿠키를 읽고, ChatStompAuthInterceptor가 해당 principal을 사용합니다.
  4. 연결되면 먼저 /user/queue/chat-errors를 구독합니다. 그 다음 등록된 방에 대해 다음 네 개의 destination을 구독합니다.
  5. /topic/chat/rooms/{roomId}의 JSON 본문은 ChatMessage로 파싱되어 onMessage로 전달됩니다.
  6. /notice는 ChatNoticeUpdate, /updates는 ChatMessageUpdate, /read는 ChatReadReceipt로 파싱되어 각각의 핸들러에 전달됩니다.
  7. 연결이 끊겼다가 다시 연결되면 onReconnect()가 호출됩니다. 클라이언트 주석은 토픽이 끊긴 동안의 메시지가 다시 전송되지 않으므로 이 시점에 최신 페이지를 REST로 조회해 공백을 채워야 한다고 설명합니다.
  8. 메시지 전송은 STOMP SEND가 아니라 POST /chat/rooms/{id}/messages REST 요청입니다. 따라서 소켓 연결 여부와 메시지 쓰기 요청의 성공 응답이 분리됩니다.

채팅 연결은 reconnectDelay: 3_000, incoming·outgoing heartbeat 10_000ms로 설정됩니다. 연결 전 첫 시도가 성공하지 않았고 재시도하는 경우에는 refreshAccessTokenOnce()를 한 번만 실행하여 다음 핸드셰이크에 갱신된 쿠키가 포함되도록 합니다.

출처: chatSocket.ts:13-35, chatSocket.ts:132-172, chatSocket.ts:209-257, chatSocket.ts:259-302

서버 처리와 저장 경계

확인된 서버 설정은 HTTP·WebSocket 요청이 외부 자원과 만나는 경계를 보여 줍니다. 다만 실제 컨트롤러가 어떤 서비스와 엔티티를 호출하고, 데이터베이스에 어떤 순서로 저장하는지는 제공된 원문에 없습니다. 그러므로 아래 흐름은 구현된 도메인 저장 순서가 아니라 코드에서 확인되는 인프라 경계입니다.

이미지와 S3 경계

서버는 기본 S3Client, 채팅 전용 chatS3Client, S3Presigner를 빈으로 등록합니다.

빈리전API 호출 제한
s3ClientRegion.AP_NORTHEAST_2전체 30초, 시도별 15초
chatS3ClientRegion.AP_NORTHEAST_2전체 3초, 시도별 2초
s3PresignerRegion.AP_NORTHEAST_2코드에 별도 호출 타임아웃이 지정되지 않았습니다.

chatS3Client는 STOMP inbound 채널에서 실행되는 채팅 이미지 검증의 headObject 지연을 줄이기 위한 별도 클라이언트로 설명되어 있습니다. 실제 이미지 업로드 API, presigned URL 발급 컨트롤러, 객체 키를 도메인 데이터에 저장하는 서비스는 3.4 이미지 업로드와 서버 도메인 소스 확인이 필요합니다.

푸시와 비동기 경계

FirebaseConfig는 FIREBASE_SERVICE_ACCOUNT_JSON 값이 비어 있으면 FirebaseMessaging을 만들지 않고 부팅을 계속합니다. 값이 {로 시작하면 JSON 내용으로, 그렇지 않으면 파일 경로로 읽습니다. 초기화에 실패해도 FCM 발송을 비활성화하고 서버 부팅은 계속됩니다.

AsyncConfig의 pushExecutor는 @EnableAsync와 함께 등록되며 코어 스레드 2개, 최대 4개, 큐 용량 500을 사용합니다. 어떤 서비스 메서드가 이 executor를 호출하는지는 제공된 코드에 없으므로, REST 요청 처리 후 어떤 이벤트가 푸시로 이어지는지는 확인이 필요합니다.

출처: S3Config.java:11-47, FirebaseConfig.java:16-53, AsyncConfig.java:9-25

오류·재시도·관측 흐름

클라이언트와 서버는 오류를 한 번에 동일한 방식으로 처리하지 않습니다. HTTP에서는 Axios 응답 인터셉터가 인증 만료를 식별하고, WebSocket에서는 STOMP 에러 큐의 프레임을 채팅 오류 모델로 변환합니다.

오류 조건클라이언트 처리
HTTP 401 또는 302_retried가 없는 요청에 한해 refreshAccessTokenOnce() 후 원래 요청을 한 번 재시도합니다.
토큰 재발급 실패원래 오류를 거부 상태로 반환합니다. silentAuthClient 호출부는 직접 로그아웃 처리를 해야 합니다.
채팅 에러 프레임parseError()가 {code,message}와 x-failed-destination을 ChatSocketError로 변환합니다.
CHAT-015retryAfterSeconds 또는 기본 5초만큼 연결을 중지한 뒤 다시 활성화합니다.
특정 방 destination 구독 거절해당 destination만 제거하고, 권한 없는 방 때문에 전체 연결을 무한 재접속하지 않습니다.
메시지 본문 토픽 구독 거절blocksMessages()가 true를 반환합니다. /notice, /updates, /read만 막힌 경우 메시지 수신 중단으로 간주하지 않습니다.
WebSocket 재연결방 토픽을 다시 구독하고 onReconnect()를 호출하여 REST 최신 페이지 보정을 유도합니다.

서버의 LoggingFilter는 일반 HTTP 요청에 대해 시작 시점과 완료 시점의 로그를 기록합니다. /actuator 요청과 OPTIONS 요청은 별도 로그를 남기지 않고 필터 체인을 통과합니다. 서버의 실제 도메인 예외 응답 형식은 제공된 컨트롤러·예외 처리기 코드가 없어 이 페이지에서 확정하지 않습니다.

출처: client.ts:33-68, client.ts:23-69, chatSocket.ts:79-114, chatSocket.ts:187-207, LoggingFilter.java:20-38

확인 필요
  • 1D1S-server-v2에서 /chat/rooms/{id}/messages를 처리하는 실제 컨트롤러·서비스·리포지터리의 클래스명과 호출 순서는 무엇입니까?
  • 일반 클라이언트 화면과 1D1S-admin의 각 화면이 호출하는 구체적인 REST 엔드포인트 목록은 어디에 정의되어 있습니까?
  • requestData()가 기대하는 ApiResponse<TData>를 서버의 어떤 공통 응답 클래스와 컨트롤러가 생성합니까?
  • 챌린지, 일지, 회원, 채팅 메시지와 같은 도메인 데이터가 어떤 데이터베이스와 테이블에 저장되며, 트랜잭션 경계는 어디입니까?
  • S3Presigner가 실제로 어떤 컨트롤러와 서비스에서 사용되며, presigned URL 발급 뒤 객체 URL 또는 키가 어떤 엔티티 필드에 저장됩니까?
  • 채팅 이미지 검증의 headObject 호출과 메시지 저장 사이의 실패·재시도·트랜잭션 처리는 어떻게 구현되어 있습니까?
  • ChatStompAuthInterceptor의 실제 패키지와 JwtAuthenticationFilter가 WebSocket 핸드셰이크에서 쿠키를 읽는 구체적인 코드는 어디에 있습니까?
  • FirebaseMessaging을 사용하는 서비스와 pushExecutor를 지정하는 @Async 메서드는 무엇입니까?
  • API_BASE_URL, DEFAULT_TIMEOUT_MS, CORS_EXTRA_ALLOWED_ORIGINS, firebase.service-account-json의 환경별 실제 값과 배포 주입 위치는 어디입니까?

출처: request.ts:36-57, chatSocket.ts:18-35, SecurityConfig.java:42-49, FirebaseConfig.java:24-45

속성
분류스펙
폴더AI 역문서 / 1. 개요
#AI 역문서
연결된 화면·API0
아직 없음
이 문서를 언급한 문서
7
3.2 API와 응답 구조스펙
2.7 홈과 콘텐츠 탐색스펙
2.5 채팅과 신고스펙
2.6 마이페이지와 활동스펙
0 목차스펙
1.2 저장소 구조스펙
1.1 서비스와 레포 개요스펙
#AI 역문서
연결된 일정1
9/30검토역문서화 위키 검토