This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Kokomen (꼬꼬면) is an AI-powered mock interview platform for developers. The name comes from "꼬리에 꼬리를 무는 면접 질문" (follow-up questions that chain together).
# Full build
./gradlew clean build
# Run server
./gradlew bootRun
# Run all tests
./gradlew test
# Run single test class
./gradlew test --tests "com.samhap.kokomen.interview.service.core.InterviewServiceTest"
# Run single test method
./gradlew test --tests "com.samhap.kokomen.interview.service.core.InterviewServiceTest.메소드명"
# Start test infrastructure (MySQL + Redis)
docker compose -f test.yml up -d
# Or use the helper script
./run-test-mysql-redis.sh- Java 17, Spring Boot 3.x
- MySQL 8.0 (Primary DB), Redis/Valkey (Session & Cache, Redisson for distributed locks)
- OpenAI GPT-4 / AWS Bedrock for AI features
- Supertone for TTS (voice mode)
- Kakao/Google OAuth for authentication
- Flyway for DB migrations (
src/main/resources/db/migration/) - Spring REST Docs for API documentation
- Micrometer + Prometheus for metrics (management port: 8081)
src/main/java/com/samhap/kokomen/
├── admin/ # Admin operations (root question voice management)
├── answer/ # Interview answers, likes, memos
├── auth/ # OAuth login (Kakao, Google)
├── category/ # Interview categories
├── global/ # Cross-cutting concerns (AOP, config, exceptions, fixtures)
├── interview/ # Core interview domain (start, proceed, questions, resume-based)
├── member/ # User profiles and scores
├── payment/ # Tosspayments integration (payments, webhooks, refunds)
├── product/ # Purchasable products
├── recruit/ # Job recruitment listings
├── resume/ # Resume/portfolio upload and AI evaluation
└── token/ # Interview tokens (purchase, consumption)
{domain}/
├── controller/
├── service/
│ └── dto/ # Request/Response DTOs (suffixed with Request or Response)
├── repository/
│ └── dto/ # Query projections
├── entity/ # JPA entities
├── domain/ # Domain logic & enums
├── tool/ # Utility classes
└── external/ # External API clients
The interview domain uses sub-packages to organize its service layer. Facade services and InterviewQueryService remain at the root for controller access:
interview/service/
├── InterviewStartFacadeService # Orchestrates interview start flow
├── InterviewProceedFacadeService # Orchestrates answer submission flow
├── InterviewQueryService # Read-only query delegation for controllers
├── core/ # InterviewService, InterviewProceedService
├── question/ # QuestionService, RootQuestionService, QuestionGeneration*
├── resume/ # ResumeBasedInterviewService, ResumeContentService
├── social/ # InterviewLikeService, InterviewViewCountService
├── infra/ # InterviewSchedulerService, InterviewProceedBedrockFlowAsyncService
└── dto/
- Facade pattern: Domains with complex orchestration use
*FacadeServiceclasses. Other domains should depend on facade services, not internal services directly. - Custom annotations:
@DistributedLock(Redis-based),@ExecutionTimer,@RedisExceptionWrapper - Base entity:
BaseEntitywith@CreatedDate createdAt
- Follows Woowacourse Java Style Guide (based on Google Java Style)
- Column limit: 120 characters
- Indent: 4 spaces, continuation indent: +8 spaces minimum
- Methods:
행위 + 도메인(e.g.,saveMember()) read-prefix: value must exist, throws exception if not foundfind-prefix: value may not exist, returns Optional or empty list- Don't use
get-for non-getter methods - DTOs end with
RequestorResponse
- Lombok → Spring annotations (more important annotations go below)
@Lombok
@SpringAnnotation
public void example() {}- Constructor
- Static factory methods
- Business methods (CRUD order, private methods after their calling public method)
- Override methods (equals, hashCode, toString)
- Custom exceptions:
BadRequestException,UnauthorizedException,ForbiddenException, etc. - Validation:
@Validin DTO, entity-level validation in constructors - Business validation that needs external data goes in service layer
- Test method names in Korean
- No
@DisplayNameannotation - Controller tests: MockMvc + real beans (integration test, generates RestDocs)
- Service tests: integration with repository
- Domain tests: unit tests
- Test isolation:
MySQLDatabaseCleanertruncates all tables (not@Transactional) - Fixtures:
global/fixture/{domain}/XxxFixtureBuilder— staticbuilder(), fluent setters, sensible defaults
Tests require MySQL and Redis containers:
- MySQL: port 13306 (database: kokomen-test, password: root)
- Redis: port 16379
Start with: docker compose -f test.yml up -d
BaseTest:@SpringBootTestwith@ActiveProfiles("test"), mocks external services (GPT, S3, Supertone, Tosspayments, OAuth clients, Bedrock), spies on Redis. Uses real MySQL container +MySQLDatabaseCleaner.BaseControllerTest: Extends BaseTest, adds MockMvc with RestDocs configuration.DocsTest:@ActiveProfiles("docs"), uses H2 in-memory DB with@Transactional. For lightweight REST Docs generation without Docker.
Required for local development:
OPEN_AI_API_KEY
KAKAO_CLIENT_ID
KAKAO_CLIENT_SECRET
GOOGLE_CLIENT_ID
GOOGLE_CLIENT_SECRET
SUPERTONE_API_TOKEN
local: Local developmentdev: Development serverprod: Productionload-test: Load testingtest: Test environment (real MySQL + Redis containers)docs: REST Docs generation (H2 in-memory, no Docker needed)