-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: Global Exception Handling #3
Changes from all commits
4f31848
add0b23
c93096d
6cc890f
dd8a928
4a05c13
9d1f4c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,19 @@ | ||
## <i>PULL REQUEST</i> | ||
|
||
### ๐ ์์ ์ค์ธ ๋ธ๋์น | ||
### ๐ Issue Ticket | ||
- | ||
|
||
### ๐ ์ฃผ์ ์์ ์ฌํญ | ||
- | ||
|
||
### ๐ (Optional) ์ฐธ๊ณ ์๋ฃ | ||
- ์คํฌ๋ฆฐ์ท์ ์ฒจ๋ถํด์ฃผ์ธ์. | ||
|
||
### ๊ด๋ จ ์ด์ | ||
- | ||
|
||
### (์ค์) ์๋ธ๋ชจ๋์ด ์์ ๋์๋์? | ||
- ๊ธฐ์กด ์ปค๋ฐ : | ||
- ๋ณ๊ฒฝ ์ปค๋ฐ : | ||
- ๋ณ๊ฒฝ ์ฌํญ : | ||
- [] ํด๋น ์๋ธ๋ชจ๋ ๋ณ๊ฒฝ์ฌํญ์ด PR์ ์ ๋ฐ์๋์๋์? | ||
|
||
### ๊ผญ ํ์ธํด ์ฃผ์ธ์!! | ||
- | ||
- |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,35 @@ | ||
name: ๋น๋ ํ ์คํธ / ๋ฆฌ๋ทฐ์ด ํ ๋น | ||
name: ํ ์คํธ ์ฝ๋ ๋ฐ ๋น๋ | ||
|
||
on: | ||
pull_request: | ||
types: [opened, synchronize, closed] | ||
types: [ opened, synchronize, closed ] | ||
branches: | ||
- 'develop' | ||
- 'master' | ||
|
||
jobs: | ||
build_and_review_assign: | ||
name: "[CI] Check Build/Testcases and Assign Reviewer" | ||
test: | ||
name: "[CI] Check Tests" | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: (Set Up) checkout | ||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
with: | ||
token: ${{ secrets.GIT_TOKEN }} | ||
submodules: true | ||
|
||
- name: (Set Up) Set up JDK 17 | ||
- name: Setup Java 17 | ||
uses: actions/setup-java@v3 | ||
with: | ||
java-version: '17' | ||
distribution: 'temurin' | ||
distribution: 'adopt' | ||
|
||
- name: (Set Up) Grant Execute permission for gradlew | ||
run: chmod 777 gradlew | ||
- name: Update Git submodules | ||
run: git submodule update --remote --recursive | ||
|
||
- name: (Build) Build with Gradle | ||
id: build | ||
run: ./gradlew test -i | ||
- name: Grant execute permission for gradlew | ||
run: chmod +x gradlew | ||
|
||
- name: (Assign Reviewer) | ||
if: steps.build.outcome == 'success' | ||
uses: hkusu/review-assign-action@v1 | ||
with: | ||
assignees: ${{ github.actor }} | ||
reviewers: HyungJu, h-beeen | ||
ready-comment: '์ฝ๋ ๋ฆฌ๋ทฐ ์์ฒญํฉ๋๋ค ๐ <reviewers>' | ||
merged-comment: '์ฑ๊ณต์ ์ผ๋ก Merge ๋์์ต๋๋ค. Shout out to <reviewers> :wink:' | ||
- name: Test with Gradle | ||
run: ./gradlew test |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.vacgom.backend.domain.member | ||
|
||
import com.vacgom.backend.global.auditing.BaseEntity | ||
import jakarta.persistence.* | ||
|
||
@Entity | ||
@Table(name = "t_member") | ||
class Member(nickname: String) : BaseEntity() { | ||
|
||
@Id | ||
@Column(name = "member_id") | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
var id: Long? = null | ||
|
||
@Column(name = "nickname") | ||
val nickname: String | ||
|
||
init { | ||
this.nickname = nickname | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.vacgom.backend.global.exception | ||
|
||
import com.vacgom.backend.global.exception.error.BusinessException | ||
import com.vacgom.backend.global.exception.error.ErrorCode | ||
import com.vacgom.backend.global.exception.error.ErrorResponse | ||
import com.vacgom.backend.global.exception.error.GlobalError | ||
import com.vacgom.backend.global.logger.CommonLogger | ||
import org.springframework.http.HttpStatus | ||
import org.springframework.http.ResponseEntity | ||
import org.springframework.web.bind.MethodArgumentNotValidException | ||
import org.springframework.web.bind.MissingServletRequestParameterException | ||
import org.springframework.web.bind.annotation.ExceptionHandler | ||
import org.springframework.web.bind.annotation.ResponseStatus | ||
import org.springframework.web.bind.annotation.RestControllerAdvice | ||
|
||
@RestControllerAdvice | ||
class ApiExceptionHandler { | ||
companion object : CommonLogger(); | ||
|
||
@ResponseStatus(HttpStatus.BAD_REQUEST) | ||
@ExceptionHandler(MethodArgumentNotValidException::class) | ||
fun handleMethodArgumentNotValidException(ex: MethodArgumentNotValidException): ErrorResponse? { | ||
return ErrorResponse(ex.fieldErrors) | ||
} | ||
|
||
@ResponseStatus(HttpStatus.BAD_REQUEST) | ||
@ExceptionHandler(MissingServletRequestParameterException::class) | ||
fun missingServletRequestParameterException(): ErrorResponse? { | ||
return ErrorResponse(GlobalError.INVALID_REQUEST_PARAM) | ||
} | ||
|
||
@ExceptionHandler(BusinessException::class) | ||
fun handleBusinessException( | ||
exception: BusinessException | ||
): ResponseEntity<ErrorResponse?> { | ||
logBusinessException(exception) | ||
return convert(exception.errorCode) | ||
} | ||
|
||
private fun convert( | ||
errorCode: ErrorCode | ||
): ResponseEntity<ErrorResponse?> { | ||
return ResponseEntity | ||
.status(errorCode.status) | ||
.body(ErrorResponse(errorCode)) | ||
} | ||
|
||
private fun logBusinessException(exception: BusinessException) { | ||
if (exception.errorCode.status.is5xxServerError) { | ||
log.error("", exception) | ||
} else { | ||
log.error("Error Message = {}", exception.message) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.vacgom.backend.global.exception | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import com.vacgom.backend.global.exception.error.BusinessException | ||
import com.vacgom.backend.global.exception.error.ErrorResponse | ||
import jakarta.servlet.FilterChain | ||
import jakarta.servlet.ServletException | ||
import jakarta.servlet.http.HttpServletRequest | ||
import jakarta.servlet.http.HttpServletResponse | ||
import org.springframework.http.HttpStatus | ||
import org.springframework.http.MediaType | ||
import org.springframework.stereotype.Component | ||
import org.springframework.web.filter.OncePerRequestFilter | ||
import java.io.IOException | ||
|
||
|
||
@Component | ||
class ApiExceptionHandlingFilter( | ||
private val om: ObjectMapper | ||
) : OncePerRequestFilter() { | ||
|
||
@Throws(ServletException::class, IOException::class) | ||
override fun doFilterInternal( | ||
request: HttpServletRequest, | ||
response: HttpServletResponse, | ||
chain: FilterChain | ||
) { | ||
try { | ||
chain.doFilter(request, response) | ||
} catch (exception: BusinessException) { | ||
setErrorResponse(response, exception) | ||
} | ||
} | ||
|
||
private fun setErrorResponse( | ||
response: HttpServletResponse, | ||
exception: BusinessException | ||
) = try { | ||
response.contentType = MediaType.APPLICATION_JSON_VALUE | ||
response.status = HttpStatus.UNAUTHORIZED.value() | ||
om.writeValue(response.outputStream, ErrorResponse(exception.errorCode)) | ||
} catch (exception: IOException) { | ||
throw RuntimeException(exception) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.vacgom.backend.global.exception.error | ||
|
||
|
||
class BusinessException( | ||
val errorCode: ErrorCode | ||
) : RuntimeException(errorCode.message) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.vacgom.backend.global.exception.error | ||
|
||
import org.springframework.http.HttpStatus | ||
|
||
interface ErrorCode { | ||
val message: String | ||
val status: HttpStatus | ||
val code: String | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.vacgom.backend.global.exception.error | ||
|
||
import org.springframework.validation.FieldError | ||
import java.time.LocalDateTime | ||
|
||
data class ErrorResponse( | ||
val timeStamp: String = LocalDateTime.now().toString(), | ||
val errorCode: String, | ||
val errorMessage: String, | ||
val details: Any? = null | ||
) { | ||
constructor( | ||
errorCode: ErrorCode | ||
) : this( | ||
errorCode = errorCode.code, | ||
errorMessage = errorCode.message | ||
) | ||
|
||
constructor( | ||
errorCode: ErrorCode, | ||
details: Any? | ||
) : this( | ||
errorCode = errorCode.code, | ||
errorMessage = errorCode.message, | ||
details = details | ||
) | ||
Comment on lines
+12
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ์ ๋ java๋ฅผ ์ฝ๋ฉํ ๋, 2๊ฐ ์ด์์ ํ๋ผ๋ฏธํฐ๊ฐ ๋ค์ด์ค๋ ๋ฉ์๋์ ์ฝํ๋ฆฐ์ ์ด๋ฐ ๋ถ๋ถ์ ๋ํด์ ์ด๋ค์์ผ๋ก ์ฒ๋ฆฌํ๋๊ฒ ๊ฐ์ฅ ์์ฐ์ค๋ฌ์ธ๊น์? public void singleParameter(Long id){
id++;
}
public void multipleParameter(
Long id1,
Long id2,
Long id3
) {
id1++;
id2--;
id3 -= 1;
} |
||
|
||
constructor( | ||
fieldError: FieldError? | ||
) : this( | ||
errorCode = fieldError?.code ?: "", | ||
errorMessage = fieldError?.defaultMessage ?: "" | ||
) | ||
|
||
constructor(fieldErrors: List<FieldError>) : this( | ||
GlobalError.INVALID_REQUEST_PARAM, | ||
fieldErrors.associate { | ||
it.field to (it.defaultMessage ?: "null") | ||
} | ||
) | ||
Comment on lines
+12
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ์ฝํ๋ฆฐ์์ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋์ ์ฌ์ฉ์ ์ด๋ป๊ฒ ์๊ฐํ์ธ์? ๊ทธ๋์ of๋ก ์ค๋ฒ๋ก๋ฉํด์ ์ฌ์ฉํ๋ ๋ฉ์๋๋ฅผ ์ ๋ถ ์์ฑ์๋ก ๋ฐ๊ฟ ์ค๊ณํด๋ดค์ด์. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.vacgom.backend.global.exception.error | ||
|
||
import org.springframework.http.HttpStatus | ||
|
||
enum class GlobalError( | ||
override val message: String, | ||
override val status: HttpStatus, | ||
override val code: String | ||
) : ErrorCode { | ||
GLOBAL_NOT_FOUND("๋ฆฌ์์ค๊ฐ ์กด์ฌํ์ง ์์ต๋๋ค.", HttpStatus.NOT_FOUND, "G_001"), | ||
INVALID_REQUEST_PARAM("์์ฒญ ํ๋ผ๋ฏธํฐ๊ฐ ์ ํจํ์ง ์์ต๋๋ค.", HttpStatus.BAD_REQUEST, "G_002"); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.vacgom.backend.global.logger | ||
|
||
import org.slf4j.LoggerFactory | ||
|
||
abstract class CommonLogger { | ||
val log = LoggerFactory.getLogger(this.javaClass)!! | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try-catch๋ ์ ๊ธฐํ ๋ฌธ๋ฒ์ผ๋ก ์ฐ๋๊ตฐ์.. ์ ์ธ๊ณ๋ฅผ ๋ง๋ณด๋์ค