Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,175 changes: 3,175 additions & 0 deletions backend/openapi.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.stackup.stackup.common.openapi;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.stackup.stackup.auth.domain.OAuthStateRepository;
import com.stackup.stackup.auth.domain.RefreshTokenRepository;
import com.stackup.stackup.common.messaging.domain.ProcessedMessageRepository;
import com.stackup.stackup.document.domain.AnalyzedDocumentRepository;
import com.stackup.stackup.document.domain.DocumentEmbeddingRepository;
import com.stackup.stackup.github.domain.GithubRepositoryRepository;
import com.stackup.stackup.log.ai.domain.AiRequestLogRepository;
import com.stackup.stackup.resume.domain.ResumeRepository;
import com.stackup.stackup.session.domain.InterviewMessageRepository;
import com.stackup.stackup.session.domain.InterviewSessionRepository;
import com.stackup.stackup.session.domain.MessageVoiceAnalysisRepository;
import com.stackup.stackup.session.domain.SessionContextRepository;
import com.stackup.stackup.session.domain.SessionFeedbackRepository;
import com.stackup.stackup.user.domain.UserRepository;
import com.stackup.stackup.user.domain.consent.UserConsentRepository;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

/**
* OpenAPI 스펙을 정적 파일(backend/openapi.json)로 산출하는 테스트.
*
* <p>프론트엔드는 이 커밋된 파일에서 openapi-typescript 로 타입을 생성하므로, 로컬 백엔드 기동 없이도 타입 안정성을 얻는다. 백엔드
* 계약을 바꾸면 이 테스트를 다시 실행해 openapi.json 을 갱신·커밋한다. (CI 에서 "커밋된 스펙이 최신인지" 검증하는 가드로 확장
* 가능 — 생성 결과와 커밋본을 비교.)
*
* <p>test 프로파일은 application-test.yml 의 더미 secret 으로 전체 컨텍스트를 로드하고, 모든 repository 는
* {@link StackupApplicationTests} 와 동일하게 mock 처리해 실 DB 없이 springdoc 의 매핑·스키마만으로 스펙을
* 렌더한다.
*/
@SpringBootTest
class OpenApiSpecExportTest {

private static final Path OUTPUT = Path.of("openapi.json");

@Autowired private WebApplicationContext webApplicationContext;

@MockitoBean private OAuthStateRepository oauthStateRepository;
@MockitoBean private RefreshTokenRepository refreshTokenRepository;
@MockitoBean private UserRepository userRepository;
@MockitoBean private ResumeRepository resumeRepository;
@MockitoBean private GithubRepositoryRepository githubRepositoryRepository;
@MockitoBean private AnalyzedDocumentRepository analyzedDocumentRepository;
@MockitoBean private ProcessedMessageRepository processedMessageRepository;
@MockitoBean private DocumentEmbeddingRepository documentEmbeddingRepository;
@MockitoBean private UserConsentRepository userConsentRepository;
@MockitoBean private InterviewSessionRepository interviewSessionRepository;
@MockitoBean private InterviewMessageRepository interviewMessageRepository;
@MockitoBean private SessionContextRepository sessionContextRepository;
@MockitoBean private SessionFeedbackRepository sessionFeedbackRepository;
@MockitoBean private MessageVoiceAnalysisRepository messageVoiceAnalysisRepository;
@MockitoBean private AiRequestLogRepository aiRequestLogRepository;

@Test
void exportOpenApiSpec() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
MvcResult result =
mockMvc.perform(get("/api/v3/api-docs")).andExpect(status().isOk()).andReturn();

byte[] raw = result.getResponse().getContentAsByteArray();
ObjectMapper objectMapper = new ObjectMapper();
Object tree = objectMapper.readValue(raw, Object.class);
String pretty = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(tree);

Files.writeString(OUTPUT, pretty + System.lineSeparator(), StandardCharsets.UTF_8);
}
}
23 changes: 13 additions & 10 deletions frontend/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
| Language | TypeScript | 5.9.x |
| Lint | ESLint 9 (flat config) + Prettier | |
| Module | ESM (`"type": "module"`) | |
| Router | React Router | 7.x (도입) |
| Server State | TanStack Query | 5.x (도입) |
| Styling | Tailwind CSS v4 (`@theme` 토큰) | 4.x (도입) |
| API 타입 | openapi-typescript → `shared/api/generated.ts` | (도입, §7.1) |
| 테스트 | Vitest + Testing Library (jsdom) | (도입) |

### 미정 (Phase 1 도입 시점에 결정)
- Router: 후보 — React Router v7 / TanStack Router
- Server State: TanStack Query
- Form: React Hook Form + Zod
- Styling: CSS Variables 기반 + Tailwind 또는 vanilla-extract
- 테스트: Vitest + Testing Library + Playwright(E2E)
### 미정 (도입 시점에 결정)
- Form: React Hook Form + Zod (현재 controlled `useState` 검증으로 충분 — 폼 복잡도 증가 시 도입)
- E2E: Playwright

> 신규 의존성 추가 시 [`/docs/coding-conventions.md §6`](../docs/coding-conventions.md) 절차를 따르고 본 표를 갱신.

Expand Down Expand Up @@ -274,8 +276,9 @@ npm run lint # ESLint

---

## 16. 현재 상태 메모 (2026-04 기준)
## 16. 현재 상태 메모 (2026-06 기준)

- `App.tsx`는 Vite 기본 데모 상태 → 첫 페이지(Login) 구현 시 교체 예정
- 라이브러리 결정 전 (라우터, server state, styling) → 첫 PR 시 의사결정 + 본 문서 §1 갱신
- 디자인 시스템 토큰 파일 미생성 → 디자인 시스템 첫 적용 PR에서 `app/styles/tokens.css` 생성
- 라이브러리 결정 완료: React Router v7, TanStack Query v5, Tailwind v4, Vitest+Testing Library (§1).
- 디자인 토큰 `app/styles/tokens.css` 생성됨. `shared/ui` 프리미티브: StatusBadge, Button, Spinner, TextArea, RadioCardGroup, Stepper.
- **텍스트 면접 세션 구현됨**: `/sessions/new`(생성 설정), `/sessions/:id`(라이브 진행). `features/interview` + `domain/session`. 라이브는 WS(`useInterviewSocket`) 알림 + `GET messages` 쿼리 본문 소유 + 낙관적 답변 (`useLiveInterview`). TTS 재생·음성 답변(RT3)은 범위 밖.
- **OpenAPI 타입 파이프라인**: 백엔드가 `OpenApiSpecExportTest`로 `backend/openapi.json` 산출·커밋 → 프론트 `npm run openapi`(= `openapi-typescript ../backend/openapi.json …`)로 `shared/api/generated.ts` 생성. 프론트는 백엔드 런타임에 무의존. 계약 변경 시 백엔드 테스트 재실행으로 갱신.
Loading