# Conventions — bibimbap

## Mapper (MyBatis)
- `@Mapper` 인터페이스 + 인라인 `@Select/@Insert/@Update/@Delete` (Java text block SQL). XML 매퍼·네임스페이스 없음.
- SELECT 에서 snake_case 컬럼을 camelCase 로 alias (`g.user_id AS userId`).
- 다중/명명 인자에 `@Param("x")`. 자동생성 PK: `@Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id")`.
- 모든 쿼리에 soft-delete 가드 `is_delete IS NOT TRUE` (조인 대상 포함).

## Controller
- 생성자 주입(필드 `@Autowired` 미사용).
- 페이지 `@Controller`: view-name String 반환 (`"recruit-list"`, `"redirect:/login"`).
- JSON 엔드포인트: `ResponseEntity<Map<String,Object>>` — `LinkedHashMap` 에 `{status, message, ...}`. 헬퍼 `response(HttpStatus, msg)`.
- mutation(`@PostMapping`/`@DeleteMapping`)은 `@Transactional` + 표준 가드 순서:
  1. `if (!CsrfTokens.isValid(request)) return 403 + CsrfTokens.errorBody()`
  2. `sessionUserId(session)` null → 401
  3. 소유권 불일치 → 403
- 입력 위생: `trimToNull`/`trimToEmpty` 헬퍼, 길이 상한 인라인 검사, `safeExternalUrl` 은 http/https 만 허용, 에셋 경로는 `/game/` 시작 + `..` 금지.

## 기타
- 사용자 노출 문자열 한국어.
- `data/` POJO 는 수기 getter/setter.
- soft-delete / 세션 인증 불변식은 `mem:core`.
