본문으로 건너뛰기

단가 제출 API

엔드포인트 목록

메서드경로인증설명
POST/v1/submissionsBearer JWT 필요단가 제보 생성
GET/v1/submissions/{id}Bearer JWT 필요단가 제보 단건 조회
DELETE/v1/submissions/{id}Bearer JWT 필요단가 제보 삭제 (소프트 삭제)

@SecurityRequirement(name = "BearerAuth") 가 컨트롤러 클래스에 선언되어 있어 모든 엔드포인트에 인증이 필요하다.


POST /v1/submissions

단가 제보를 생성한다.

요청

Content-Type: application/json

헤더:

Authorization: Bearer <JWT>

요청 바디:

필드타입필수제약설명
jobCategoryIdLong필수직무 카테고리 ID
experienceLevelIdLong필수경력 수준 ID
userIdLong선택연결할 사용자 ID. 없으면 null
submissionTypeString필수TRACK_A | TRACK_B제보 트랙
workFormatString필수ON_SITE | REMOTE | HYBRID근무 형태
durationString선택최대 50자계약 기간
amountInteger필수최솟값 10단가 금액 (만원)
amountUnitString필수MONTHLY | TOTAL금액 단위
sessionIdUUID필수클라이언트 세션 ID

duration 필드에 사용 가능한 문자열 값 (amountUnit = TOTAL 일 때 월 환산에 사용):

환산 개월 수
1주일 이하0.25개월
2~3주0.625개월
1개월1개월
2~3개월2.5개월
3개월 이상3개월
숫자 문자열해당 숫자를 개월 수로 파싱

위 목록에 없는 문자열이 오거나 duration 이 null이면 normalizedMonthly 는 계산되지 않는다 (null 반환).

요청 예시:

{
"jobCategoryId": 1,
"experienceLevelId": 2,
"userId": 12,
"submissionType": "TRACK_A",
"workFormat": "REMOTE",
"duration": "2~3개월",
"amount": 500,
"amountUnit": "TOTAL",
"sessionId": "550e8400-e29b-41d4-a716-446655440000"
}

검증 규칙

검증은 두 단계에서 수행된다.

1단계 — Bean Validation (RateSubmissionRequest.java):

  • amount: @Min(10) — 10 미만이면 400 Bad Request 반환.
  • 필수 필드 누락: @NotNull 위반 시 400 Bad Request 반환.

2단계 — 서비스 레이어 (RateSubmissionService.java, lines 51–54):

Integer normalized = submission.getNormalizedMonthly();
if (normalized != null && (normalized < 10 || normalized > 9999)) {
throw new IllegalArgumentException("환산 월 단가는 10~9,999만원 범위여야 합니다.");
}

normalizedMonthly 가 계산된 경우 10~9,999만원 범위를 벗어나면 400 Bad Request 반환. normalizedMonthly 가 null이면 범위 검사를 건너뛴다.

normalizedMonthly 계산 로직

src/main/java/com/olma/domain/entity/RateSubmission.java (lines 92–103):

  • amountUnit = MONTHLY: normalizedMonthly = amount
  • amountUnit = TOTAL: normalizedMonthly = amount / parsedMonths (HALF_UP 반올림)
  • duration 파싱 실패 또는 null: normalizedMonthly = null

응답

HTTP 201 Created

{
"id": 45,
"jobCategoryName": "UI/UX 디자이너",
"experienceLevelLabel": "3~5년",
"submissionType": "TRACK_A",
"workFormat": "REMOTE",
"duration": "2~3개월",
"amount": 500,
"amountUnit": "TOTAL",
"normalizedMonthly": 200,
"createdAt": "2026-06-27T12:34:56.789+09:00"
}
필드타입설명
idLong생성된 제보 ID
jobCategoryNameString직무 카테고리명
experienceLevelLabelString경력 수준 레이블
submissionTypeString제보 트랙
workFormatString근무 형태
durationString계약 기간 (nullable)
amountInteger입력 단가 금액
amountUnitString금액 단위
normalizedMonthlyInteger환산 월 단가 (nullable)
createdAtOffsetDateTime생성 시각 (KST offset 포함)

오류 응답

HTTP 상태원인
400Bean Validation 실패 (amount < 10 또는 필수 필드 누락)
400normalizedMonthly 가 10~9,999 범위를 벗어남
400존재하지 않는 jobCategoryId 또는 experienceLevelId
401JWT 미제공 또는 유효하지 않은 JWT

GET /v1/submissions/{id}

단가 제보를 단건 조회한다.

요청

헤더:

Authorization: Bearer <JWT>

경로 파라미터:

파라미터타입설명
idLong단가 제보 ID

응답

HTTP 200 OK — POST 응답과 동일한 RateSubmissionResponse 구조.

오류 응답

HTTP 상태원인
401JWT 미제공 또는 유효하지 않은 JWT
404해당 ID의 제보가 존재하지 않음

DELETE /v1/submissions/{id}

단가 제보를 숨김 처리한다. 물리 삭제가 아닌 소프트 삭제다 (status = HIDDEN).

요청

헤더:

Authorization: Bearer <JWT>

경로 파라미터:

파라미터타입설명
idLong단가 제보 ID

응답

HTTP 204 No Content — 응답 바디 없음.

오류 응답

HTTP 상태원인
401JWT 미제공 또는 유효하지 않은 JWT
404해당 ID의 제보가 존재하지 않음

로그

제보 생성 성공 시 다음 로그가 남는다 (RateSubmissionService.java line 57):

INFO rate submission created submissionId=45 userId=12

userId 는 요청 바디의 userId 값이다. 비로그인 제보인 경우 null 로 기록된다.


CORS

src/main/java/com/olma/config/WebConfig.java (line 36):

.allowCredentials(true)

allowCredentials(true) 가 설정되어 있어 클라이언트가 쿠키 또는 Authorization 헤더를 포함한 크로스 오리진 요청을 보낼 수 있다. allowedOriginPatterns("*") 와 함께 사용 중이다.