본문 바로가기

카테고리 없음

24.02.01 복습3

  • 2/1 - 전체 게시글 목록 조회 API
    • [ ] 제목, 작성자명(nickname), 작성 날짜를 조회하기
    • [ ] 작성 날짜 기준으로 내림차순 정렬하기
  • 💬 2/2 - 게시글 작성 API
    • [ ] 토큰을 검사하여, 유효한 토큰일 경우에만 게시글 작성 가능
    • [ ] 제목(500자 까지 입력 가능), 작성 내용을 입력하기(5000자 까지 입력 가능)
    • [ ] (챌린지 과제) 이미지 업로드 가능
  • 💬 2/5 - 게시글 조회 API
    • 제목, 작성자명(nickname), 작성 날짜, 작성 내용을 조회하기 (검색 기능이 아닙니다. 간단한 게시글 조회만 구현해주세요.)
  • 💬 2/6 - 게시글 수정 API
    • 토큰을 검사하여, 해당 사용자가 작성한 게시글만 수정 가능
  • 💬 2/7 - 게시글 삭제 API
    • [ ] 토큰을 검사하여, 해당 사용자가 작성한 게시글만 삭제 가능

이제 이것들을 만들어야 한다. 빨리 끝내고 다음 인강을 들을 시간을 내자!!

 

package com.example.demo.model

import jakarta.persistence.*
import java.time.LocalDateTime

@Entity
@Table(name = "post")
class Post(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long? = null,

    @Column(name = "title", nullable = false)
    var title: String,

    @Column(name = "nickname", nullable = false)
    var nickname: String,

    @Column(name = "content", nullable = false)
    var content: String,

    @Column(name = "created_at", nullable = false)
    var createdAt: LocalDateTime = LocalDateTime.now()
)

들어가야 하는게 제목, 닉네임, 내용, 날짜이므로 이걸 넣고

 

package com.example.demo.dto

import java.time.LocalDateTime

data class PostDto(
    val id: Long,
    val title: String,
    val nickname: String,
    val content: String,
    val createdAt: LocalDateTime
)

dto도 같은 내용들을 집어넣는다.

package com.example.demo.dto

data class PostCreateDto(
    val title: String,
    val nickname: String,
    val content: String
)

만들때 필요한건 제목, 닉네임, 내용이고

package com.example.demo.dto

data class PostUpdateDto(
    val title: String,
    val content: String
)

수정은 제목과 내용

 

interface PostRepository : JpaRepository<Post, Long> {
    fun findByTitleAndNickNameAndCreatedAt(title: String, nickName: String, createdAt: LocalDateTime): List<Post>
    fun findAllByOrderByCreatedAtDesc(): List<Post>

}

이걸로 제목, 작성자명(nickname), 작성 날짜를 조회하기 이건 위에것, 작성 날짜 기준으로 내림차순 정렬하기 이건 밑이 담당한다.

 

그런데, dto를 수정하기로 했다.

data class PostDto(
    val id: Long,
    val title: String,
    val nickname: String,
    val content: String,
    val createdAt: LocalDateTime
) {
    companion object {
        fun from(post: Post): PostDto {
            return PostDto(
                id = post.id!!,
                title = post.title,
                nickname = post.nickname,
                content = post.content,
                createdAt = post.createdAt
            )
        }
    }
}

이러면 이제 서비스임플에서 일일이

override fun getAllPosts(): List<PostDto> {
    val posts = postRepository.findAllByOrderByCreatedAtDesc()
    return posts.map { post -> 
        PostDto(
            id = post.id!!,
            title = post.title,
            nickname = post.nickname,
            content = post.content,
            createdAt = post.createdAt
        ) 
    }
}

이렇게 안하고

override fun getAllPosts(): List<PostDto> {
    val posts = postRepository.findAllByOrderByCreatedAtDesc()
    return posts.map { PostDto.from(it) }
}

이렇게 할수있다.

그리고 서비스도 fun getAllPosts(): List<PostDto>를 넣어준다.

interface PostService {
    fun createPost(createPostRequest: PostCreateDto): Post
    fun getPost(postId: Long): Post
    fun getAllPosts(): List<PostDto>
    fun updatePost(postId: Long, updatePostRequest: PostUpdateDto): Post
    fun deletePost(postId: Long)
}

이제 리스트를 dto에서 가져오기 때문이다.

 

override fun createPost(createPostRequest: PostCreateDto, token: String): Post {
    // 토큰 검사
    val nickName = jwtPlugin.validateToken(token)

    // 토큰에 담긴 닉네임으로 사용자 조회
    val member = memberRepository.findByNickName(nickName.toString())
        ?: throw IllegalArgumentException("유효하지 않은 사용자입니다.")

    // 제목과 내용의 길이 검사
    if (createPostRequest.title.length > 500) {
        throw IllegalArgumentException("제목은 500자를 초과할 수 없습니다.")
    }

    if (createPostRequest.content.length > 5000) {
        throw IllegalArgumentException("내용은 5000자를 초과할 수 없습니다.")
    }

    // 게시글 작성
    val post = Post(
        title = createPostRequest.title,
        nickname = member.nickName,
        content = createPostRequest.content,
    )

    return postRepository.save(post)
}

이렇게 했는데 override가 빨갛다!

 

보니까 서비스에서
fun createPost(createPostRequest: PostCreateDto): Post 이게 작동 안하고 회색이였다.

fun createPost(createPostRequest: PostCreateDto, token: String): Post
토큰을 넣는다!

 

게시글 수정 API

  • 토큰을 검사하여, 해당 사용자가 작성한 게시글만 수정 가능
override fun updatePost(postId: Long, updatePostRequest: PostUpdateDto, token: String): Post {
    // 토큰 검사
    val nickName = jwtPlugin.validateToken(token)

    // 토큰에 담긴 닉네임으로 사용자 조회
    val member = memberRepository.findByNickName(nickName.toString())
        ?: throw IllegalArgumentException("유효하지 않은 사용자입니다.")

    // 게시글 조회
    val post = postRepository.findById(postId)
        .orElseThrow { IllegalArgumentException("해당 게시글이 존재하지 않습니다.") }

    // 사용자 검사
    if (post.nickname != member.nickName) {
        throw IllegalArgumentException("본인이 작성한 게시글만 수정할 수 있습니다.")
    }

    // 게시글 수정
    post.title = updatePostRequest.title
    post.content = updatePostRequest.content

    return postRepository.save(post)
}

이렇게 만들고 

서비스에 토큰을 추가한다.

fun updatePost(postId: Long, updatePostRequest: PostUpdateDto, token: String): Post // 이 부분을 추가

 

@RestController
@RequestMapping("/posts")
class PostController(private val postService: PostService) {

    @PostMapping
    fun createPost(@RequestBody createPostRequest: PostCreateDto, @RequestHeader("Authorization") token: String): ResponseEntity<PostDto> {
        val post = postService.createPost(createPostRequest, token)
        val postDto = PostDto.from(post)
        return ResponseEntity.ok().body(postDto)
    }

    @GetMapping("/{postId}")
    fun getPost(@PathVariable postId: Long): ResponseEntity<PostDto> {
        val post = postService.getPost(postId)
        val postDto = PostDto.from(post)
        return ResponseEntity.ok().body(postDto)
    }

    @GetMapping
    fun getAllPosts(): ResponseEntity<List<PostDto>> {
        val posts = postService.getAllPosts()
        val postDtos = posts.map { PostDto.from(it) }
        return ResponseEntity.ok().body(postDtos)
    }

    @PutMapping("/{postId}")
    fun updatePost(@PathVariable postId: Long, @RequestBody updatePostRequest: PostUpdateDto, @RequestHeader("Authorization") token: String): ResponseEntity<PostDto> {
        val post = postService.updatePost(postId, updatePostRequest, token)
        val postDto = PostDto.from(post)
        return ResponseEntity.ok().body(postDto)
    }

    @DeleteMapping("/{postId}")
    fun deletePost(@PathVariable postId: Long, @RequestHeader("Authorization") token: String): ResponseEntity<Void> {
        postService.deletePost(postId, token)
        return ResponseEntity.noContent().build()
    }
}