Skip to content

Commit c11d636

Browse files
committed
Merge branch 'feat/27' into sandbox
2 parents b51cb7f + 132f865 commit c11d636

File tree

5 files changed

+124
-5
lines changed

5 files changed

+124
-5
lines changed

.github/workflows/link-issue.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
echo "Branch name: $BRANCH_NAME"
2020
2121
# feat/{Issue ID} 패턴에서 Issue ID 추출
22-
if [[ $BRANCH_NAME =~ ^feat/([0-9]+)(\-[0-9]+)?$ ]]; then
22+
if [[ $BRANCH_NAME =~ ^feat/([0-9]+)(.*)$ ]]; then
2323
ISSUE_NUMBER="${BASH_REMATCH[1]}"
2424
echo "issue_number=$ISSUE_NUMBER" >> $GITHUB_OUTPUT
2525
echo "Found issue number: $ISSUE_NUMBER"
@@ -31,8 +31,9 @@ jobs:
3131
- name: Check if Issue Link Already Exists
3232
if: steps.extract.outputs.issue_number != ''
3333
id: check_link
34+
env:
35+
PR_BODY: ${{ github.event.pull_request.body }}
3436
run: |
35-
PR_BODY="${{ github.event.pull_request.body }}"
3637
ISSUE_NUMBER="${{ steps.extract.outputs.issue_number }}"
3738
3839
if echo "$PR_BODY" | grep -qE "(Close[sd]?|Fix(e[sd])?|Resolve[sd]?) #$ISSUE_NUMBER"; then

adapter/rdb/src/main/resources/sql.ddl/V2_CREATE_MEETING_PARTICIPANTS.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-- Meeting 테이블
22
CREATE TABLE IF NOT EXISTS meetings (
3-
meet_id VARCHAR(10) PRIMARY KEY,
3+
meet_id VARCHAR(16) PRIMARY KEY,
44
title VARCHAR(255) NOT NULL,
55
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
66
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
@@ -10,7 +10,7 @@ CREATE INDEX IF NOT EXISTS idx_meetings_updated_at ON meetings(updated_at);
1010

1111
-- Meeting 가능한 날짜들
1212
CREATE TABLE IF NOT EXISTS meeting_dates (
13-
meet_id VARCHAR(10) NOT NULL,
13+
meet_id VARCHAR(16) NOT NULL,
1414
available_date DATE NOT NULL,
1515
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
1616
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -23,7 +23,7 @@ CREATE INDEX IF NOT EXISTS idx_meeting_dates_updated_at ON meeting_dates(updated
2323
-- Participant 테이블
2424
CREATE TABLE IF NOT EXISTS participants (
2525
participant_id BIGSERIAL PRIMARY KEY,
26-
meet_id VARCHAR(10) NOT NULL,
26+
meet_id VARCHAR(16) NOT NULL,
2727
name VARCHAR(100) NOT NULL,
2828
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
2929
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

app/api/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies {
1212
implementation(project(":core"))
1313

1414
implementation("org.springframework.boot:spring-boot-starter-web")
15+
implementation("org.springframework.boot:spring-boot-starter-security")
1516
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0")
1617

1718
implementation(platform("io.awspring.cloud:spring-cloud-aws-dependencies:2.4.0"))

app/api/src/main/kotlin/com/nomoney/api/exception/GlobalExceptionHandler.kt

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@ import com.nomoney.exception.ClientException
44
import com.nomoney.exception.NoMoneyException
55
import org.springframework.http.HttpStatus
66
import org.springframework.http.ResponseEntity
7+
import org.springframework.http.converter.HttpMessageNotReadableException
8+
import org.springframework.web.HttpMediaTypeNotSupportedException
9+
import org.springframework.web.HttpRequestMethodNotSupportedException
10+
import org.springframework.web.bind.MethodArgumentNotValidException
11+
import org.springframework.web.bind.MissingServletRequestParameterException
712
import org.springframework.web.bind.annotation.ExceptionHandler
813
import org.springframework.web.bind.annotation.RestControllerAdvice
14+
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
15+
import org.springframework.web.servlet.NoHandlerFoundException
916

1017
@RestControllerAdvice
1118
class GlobalExceptionHandler {
@@ -30,6 +37,77 @@ class GlobalExceptionHandler {
3037
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse)
3138
}
3239

40+
@ExceptionHandler(MethodArgumentNotValidException::class)
41+
fun handleMethodArgumentNotValidException(ex: MethodArgumentNotValidException): ResponseEntity<ErrorResponse> {
42+
val errors = ex.bindingResult.fieldErrors.joinToString(", ") { "${it.field}: ${it.defaultMessage}" }
43+
val errorResponse = ErrorResponse(
44+
code = "E400",
45+
message = "잘못된 요청입니다.",
46+
messageForDev = "검증 실패: $errors",
47+
)
48+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse)
49+
}
50+
51+
@ExceptionHandler(HttpMessageNotReadableException::class)
52+
fun handleHttpMessageNotReadableException(ex: HttpMessageNotReadableException): ResponseEntity<ErrorResponse> {
53+
val errorResponse = ErrorResponse(
54+
code = "E400",
55+
message = "잘못된 요청입니다.",
56+
messageForDev = "요청 본문을 읽을 수 없습니다: ${ex.message}",
57+
)
58+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse)
59+
}
60+
61+
@ExceptionHandler(MissingServletRequestParameterException::class)
62+
fun handleMissingServletRequestParameterException(ex: MissingServletRequestParameterException): ResponseEntity<ErrorResponse> {
63+
val errorResponse = ErrorResponse(
64+
code = "E400",
65+
message = "잘못된 요청입니다.",
66+
messageForDev = "필수 파라미터 누락: ${ex.parameterName} (타입: ${ex.parameterType})",
67+
)
68+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse)
69+
}
70+
71+
@ExceptionHandler(MethodArgumentTypeMismatchException::class)
72+
fun handleMethodArgumentTypeMismatchException(ex: MethodArgumentTypeMismatchException): ResponseEntity<ErrorResponse> {
73+
val errorResponse = ErrorResponse(
74+
code = "E400",
75+
message = "잘못된 요청입니다.",
76+
messageForDev = "파라미터 타입 불일치 '${ex.name}': 기대값 ${ex.requiredType?.simpleName}, 입력값 ${ex.value}",
77+
)
78+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse)
79+
}
80+
81+
@ExceptionHandler(HttpRequestMethodNotSupportedException::class)
82+
fun handleHttpRequestMethodNotSupportedException(ex: HttpRequestMethodNotSupportedException): ResponseEntity<ErrorResponse> {
83+
val errorResponse = ErrorResponse(
84+
code = "E405",
85+
message = "지원하지 않는 HTTP 메서드입니다.",
86+
messageForDev = "메서드 '${ex.method}'는 지원되지 않습니다. 지원되는 메서드: ${ex.supportedHttpMethods?.joinToString(", ")}",
87+
)
88+
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(errorResponse)
89+
}
90+
91+
@ExceptionHandler(HttpMediaTypeNotSupportedException::class)
92+
fun handleHttpMediaTypeNotSupportedException(ex: HttpMediaTypeNotSupportedException): ResponseEntity<ErrorResponse> {
93+
val errorResponse = ErrorResponse(
94+
code = "E415",
95+
message = "지원하지 않는 미디어 타입입니다.",
96+
messageForDev = "Content type '${ex.contentType}'는 지원되지 않습니다. 지원되는 타입: ${ex.supportedMediaTypes.joinToString(", ")}",
97+
)
98+
return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).body(errorResponse)
99+
}
100+
101+
@ExceptionHandler(NoHandlerFoundException::class)
102+
fun handleNoHandlerFoundException(ex: NoHandlerFoundException): ResponseEntity<ErrorResponse> {
103+
val errorResponse = ErrorResponse(
104+
code = "E404",
105+
message = "요청한 리소스를 찾을 수 없습니다.",
106+
messageForDev = "핸들러를 찾을 수 없습니다: ${ex.httpMethod} ${ex.requestURL}",
107+
)
108+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse)
109+
}
110+
33111
@ExceptionHandler(Exception::class)
34112
fun handleException(ex: Exception): ResponseEntity<ErrorResponse> {
35113
val errorResponse = ErrorResponse(
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.nomoney.api.security
2+
3+
import org.springframework.context.annotation.Bean
4+
import org.springframework.context.annotation.Configuration
5+
import org.springframework.security.config.annotation.web.builders.HttpSecurity
6+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
7+
import org.springframework.security.web.SecurityFilterChain
8+
import org.springframework.web.cors.CorsConfiguration
9+
import org.springframework.web.cors.CorsConfigurationSource
10+
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
11+
12+
@Configuration
13+
@EnableWebSecurity
14+
class SecurityConfig {
15+
16+
@Bean
17+
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
18+
return http
19+
.cors { it.configurationSource(corsConfigurationSource()) }
20+
.csrf { it.disable() }
21+
.authorizeHttpRequests { it.anyRequest().permitAll() }
22+
.build()
23+
}
24+
25+
@Bean
26+
fun corsConfigurationSource(): CorsConfigurationSource {
27+
val configuration = CorsConfiguration().apply {
28+
allowedOriginPatterns = listOf("*")
29+
allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")
30+
allowedHeaders = listOf("*")
31+
allowCredentials = true
32+
maxAge = 3600L
33+
}
34+
35+
return UrlBasedCorsConfigurationSource().apply {
36+
registerCorsConfiguration("/**", configuration)
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)