2024. 5. 30. 20:58ㆍTIL
🕘
팀 프로젝트 (뉴스피드 만들기)
💡 프로젝트 기간: 2024-05-27~2024-06-03
프로젝트 진행과정
API내 제약 구현
인증 부분 수정
제약 걸기
@valid 어노테이션을 사용하지 않고 내부로직으로 제약 걸기
package com.teamsparta.abrasax.domain.post.model
import com.teamsparta.abrasax.domain.exception.*
import com.teamsparta.abrasax.domain.helper.ListStringifyHelper
import com.teamsparta.abrasax.domain.member.model.Member
import com.teamsparta.abrasax.domain.post.comment.dto.CommentResponseDto
import com.teamsparta.abrasax.domain.post.dto.CreatePostRequestDto
import com.teamsparta.abrasax.domain.post.dto.PostResponseDto
import com.teamsparta.abrasax.domain.post.dto.PostResponseWithCommentDto
import jakarta.persistence.*
import java.time.LocalDateTime
@Entity
@Table(name = "post")
class Post(
@Column(name = "title", nullable = false)
var title: String,
@Column(name = "content", nullable = false)
var content: String,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)
val member: Member,
@ElementCollection
@CollectionTable(name = "post_tag", joinColumns = [JoinColumn(name = "post_id")])
@Column(name = "tags", nullable = false)
var tags: List<String>,
@Column(name = "created_at", nullable = false)
val createdAt: LocalDateTime,
@Column(name = "updated_at", nullable = false)
var updatedAt: LocalDateTime,
@Column(name = "deleted_at")
var deletedAt: LocalDateTime? = null,
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
fun update(newTitle: String, newContent: String, newTags: List<String>) {
// validateTitleLength(newTitle)
validateContentLength(newContent)
// validateTagListSize(newTags)
validateTagLength(newTags)
this.title = newTitle
this.content = newContent
this.tags = newTags
this.updatedAt = LocalDateTime.now()
}
fun delete() {
deletedAt = LocalDateTime.now()
}
companion object {
private fun validateTitleLength(request: CreatePostRequestDto): Boolean {
// if (title.isEmpty() || title.length > 20 ) {
// throw InvalidTitleException("제목은 비어있지 않고 20자 이하여야 합니다.")
// }
return request.title.isEmpty() || request.title.length > 20
}
private fun validateContentLength(content: String) {
if (content.isEmpty() || content.length > 1000 ) {
throw InvalidContentException("내용은 비어있지 않고 1000자 이하여야 합니다.")
}
}
private fun validateTagListSize(request: CreatePostRequestDto): Boolean {
// if (tagList.size > 5) throw InvalidTagSizeException("태그는 5개를 초과할 수 없습니다.")
return request.tags.size > 5
}
private fun validateTagLength(tagList: List<String>) {
if (tagList.any { it.length > 15 }) throw InvalidTagLengthException("태그는 15자 이하여야 합니다.")
}
fun validateCreateRequest(request: CreatePostRequestDto): List<Pair<Boolean, String>> {
// tags = ["1","2","3","4","5","6"]
// title = ""
val validations = listOf(::validateTagListSize, ::validateTitleLength)
return validations.map { Pair(it(request), it.name) }.filter { it.first == true }
}
fun of(title: String, content: String, member: Member, tags: List<String>): Post {
var validTitleLength = true
// validateTitleLength(title)
validateContentLength(content)
// validateTagListSize(tags)
validateTagLength(tags)
val timestamp = LocalDateTime.now()
return Post(
title = title,
content = content,
member = member,
tags = tags,
createdAt = timestamp,
updatedAt = timestamp,
deletedAt = null
)
}
}
}
fun Post.toPostResponseDto(): PostResponseDto {
return PostResponseDto(
id = id!!,
title = title,
content = content,
tags = tags,
authorId = member.id!!
)
}
fun Post.toPostWithCommentDtoResponse(
commentResponseDto: List<CommentResponseDto>
): PostResponseWithCommentDto {
return PostResponseWithCommentDto(
id = id!!,
title = title,
content = content,
authorId = member.id!!,
tags = tags,
comments = commentResponseDto
)
}
fun main() {
val failures = Post.validateCreateRequest(CreatePostRequestDto(title = "", content = "", tags=listOf("1","1","1","1","1","1"), authorId = 1L))
if (failures.isNotEmpty())
throw PostValidationException(failures.map{it.second})
}
오류가 발생하는 부분이 여러개여도 첫번째 오류가 발생하는 시점에 예외처리가 되기때문에 발생할 수 있는 모든 오류 부분을 알게하기 위해서
val validations = listOf(::validateTagListSize, ::validateTitleLength)
::을 이용해서 함수를 인자로 받아서 validations에 저장한다.
현재 validations 안에는 validateTagListSize함수와 validateTitleLenth함수 2개가 각각 리스트의 인자로서 저장된 상태
return validations.map { Pair(it(request), it.name) }.filter { it.first == true }
Pair()함수를 이용해서 it(request)와 it.name을 짝으로 구성시키는데,
it(request) : 함수의 요청(실패 혹은 성공 ,, 예외를 던졌는지, 안던졌는지)
it.name : 함수의 이름
.filter { it.first == true }
함수의 요청이 true라면 예외를 던졌다는 의미므로
filter를 통해서 예외처리가 발생한 함수들로만 구성됨
val failures = Post.validateCreateRequest(CreatePostRequestDto(title = "", content = "", tags=listOf("1","1","1","1","1","1"), authorId = 1L))
if (failures.isNotEmpty())
throw PostValidationException(failures.map{it.second})
(failures.isNotEmpty())
이 부분은 예외처리가 최소 1개이상 발생했다는 의미이며
throw PostValidationException(failures.map{it.second})
}
그 발생한 예외들의 이름들(it.second) 을 던진다는 의미
인증 부분 수정
package com.teamsparta.abrasax.common.security
import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter
@Component
class JwtTokenFilter(
private val jwtTokenProvider: JwtTokenProvider,
) : OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain
) {
val token = resolveToken(request)
if (token != null && jwtTokenProvider.validateToken(token)) {
val auth = jwtTokenProvider.getAuthentication(token)
SecurityContextHolder.getContext().authentication = auth
}
filterChain.doFilter(request, response)
}
private fun resolveToken(request: HttpServletRequest): String? {
val bearerToken = request.getHeader("Authorization")
return if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
bearerToken.substring(7)
} else null
}
}
상속받는 인터페이스가 UsernamePasswordAuthenticationFilter() 에서 OncePerRequestFilter()로 수정
UsernamePasswordAuthenticationFilter는 주로 로그인 인증을 처리하는 데 사용되고, 사용자 이름과 비밀번호를 사용하여 인증
OncePerRequestFilter는 각 요청마다 한 번 실행되며, 인증, 로깅, 요청/응답 변환 등 다양한 작업을 처리하는 데 사용
OncePerRequestFilter는 다양한 요청에 공통적으로 필요한 작업을 수행할 때, UsernamePasswordAuthenticationFilter는 특정 엔드포인트에서 로그인 인증을 처리할 때 사용되므로
지금 현재 작성되어있는 JwtTokenFilter와 같은 JWT 토큰 기반 인증 필터는 OncePerRequestFilter를 사용하는 것이 일반적이다.
'TIL' 카테고리의 다른 글
(알고리즘) 햄버거 만들기 (0) | 2024.06.01 |
---|---|
팀프로젝트<뉴스피드 만들기>(5일차) (0) | 2024.05.31 |
팀프로젝트<뉴스피드 만들기>(3일차) (0) | 2024.05.29 |
팀 프로젝트<뉴스피드 만들기> (2일차) (0) | 2024.05.28 |
팀 프로젝트<뉴스피드 만들기> (1일차) (0) | 2024.05.27 |