본문 바로가기

카테고리 없음

24.02.06 댓글, 게시글 수정

@RestController
@RequestMapping("/comments")
class CommentController(private val commentService: CommentService) {

    @PostMapping
    fun createComment(@AuthenticationPrincipal user: UserPrincipal, @RequestBody dto: CommentCreateDto): ResponseEntity<CommentDto> {
        val userId = user.id
        val commentDto = commentService.createComment(userId, dto)
        return ResponseEntity.ok(commentDto)
    }

    @PutMapping("/{id}")
    fun updateComment(@AuthenticationPrincipal user: UserPrincipal,@PathVariable id: Long, @RequestBody dto: CommentUpdateDto): ResponseEntity<CommentDto> {
        val commentDto = commentService.updateComment(id, dto)
        return ResponseEntity.ok(commentDto)
    }

    @GetMapping("/{id}")
    fun getComment(@PathVariable id: Long): ResponseEntity<CommentDto> {
        val commentDto = commentService.getComment(id)
        return ResponseEntity.ok(commentDto)
    }

    @DeleteMapping("/{id}")
    fun deleteComment(@AuthenticationPrincipal user: UserPrincipal,@PathVariable id: Long): ResponseEntity<Unit> {
        commentService.deleteComment(id)
        return ResponseEntity.noContent().build()
    }
}

이제 get를 제외하면 전부 인증 받아야만 사용이 가능하다. 그런데, 이러면 토큰 인증만 받으면 누구나 수정이 가능해서 안된다고 한다.

 

@DeleteMapping("/{id}")
fun deleteComment(@AuthenticationPrincipal user: UserPrincipal,@PathVariable id: Long): ResponseEntity<Unit> {
    val userId = user.id
    commentService.deleteComment(id, userId)
    return ResponseEntity.noContent().build()
}

 

이렇게 userid를 넣어야 작성한 아이디만이 삭제할수있다.

@Transactional
override fun deleteComment(id: Long, userId: Long) {
    val comment = commentRepository.findByIdAndUserId(id, userId)
        .orElseThrow { IllegalArgumentException("Comment not found or not owned by user") }

    commentRepository.deleteById(id)
}

서비스임플도 이렇게 유저아이디를 넣어준다.

그런데 findByIdAndUserId를 새로 추가했으니 그걸 리포지터리에 반영한다.

interface CommentRepository : JpaRepository<Comment, Long> {
    fun findByIdAndUserId(id: Long, userId: Long): Optional<Comment>
}

 

그리고 나머지도 이렇게 바궈야한다.

 

먼저 서비스를 바꿔준다.

interface CommentService {
    fun createComment(userId: Long, dto: CommentCreateDto): CommentDto
    fun updateComment(userId: Long, id: Long, dto: CommentUpdateDto): CommentDto
    fun getComment(id: Long): CommentDto
    fun deleteComment(userId: Long, id: Long)
}

 

이렇게 유저아이디를 다 넣어준다.

 

이제 수정, 생성을 바꿔야한다.

@PostMapping
fun createComment(@AuthenticationPrincipal user: UserPrincipal, @RequestBody dto: CommentCreateDto): ResponseEntity<CommentDto> {
    val userId = user.id
    val commentDto = commentService.createComment(userId, dto)
    return ResponseEntity.ok(commentDto)
}

@PutMapping("/{id}")
fun updateComment(@AuthenticationPrincipal user: UserPrincipal,@PathVariable id: Long, @RequestBody dto: CommentUpdateDto): ResponseEntity<CommentDto> {
    val userId = user.id
    val commentDto = commentService.updateComment(userId, id, dto)
    return ResponseEntity.ok(commentDto)
}

수정, 생성의 컨트롤러는 이렇게 

 

 

@Transactional
override fun createComment(userId: Long, dto: CommentCreateDto): CommentDto {
    val post = postRepository.findById(dto.postId)
        .orElseThrow { IllegalArgumentException("Post not found") }

    val comment = Comment(
        post = post,
        nickname = dto.nickname,
        content = dto.content
    )

    commentRepository.save(comment)

    return CommentDto.from(comment)
}

@Transactional
override fun updateComment(id: Long, dto: CommentUpdateDto): CommentDto {
    val comment = commentRepository.findById(id)
        .orElseThrow { IllegalArgumentException("Comment not found") }

    comment.content = dto.content

    return CommentDto.from(comment)
}

서비스임플은 이렇게 만든다. 그리고 코맨트 클래스에 var userId: Long,를 넣어서

 

package com.example.demo.model

import jakarta.persistence.*
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate
import java.time.LocalDateTime

@Entity
@Table(name = "comments")
data class Comment(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "post_id")
    var post: Post,

    val nickname: String,
    @Column(name = "user_id", nullable = false)
    var userId: Long,

    @Column(length = 5000)
    var content: String,

    @CreatedDate
    val createdAt: LocalDateTime = LocalDateTime.now(),

    @LastModifiedDate
    val updatedAt: LocalDateTime = LocalDateTime.now()
)

이제 유저아이디를 받아올수 있다.

 

@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(),

    @Column(name = "user_id", nullable = false)
    var userId: Long
)

 

이제 포스트도 마찬가지로 수정해주고,

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

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

 

 

 

 

 

 

 

기리고 포스트컨트롤러를 변형시킨다.

 

@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()
    }
}

이것도 @AuthenticationPrincipal user: UserPrincipal을 넣어 변형시킨다.

 

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

    @PostMapping
    fun createPost(@AuthenticationPrincipal user: UserPrincipal, @RequestBody createPostRequest: PostCreateDto
    ): ResponseEntity<Post> {
        val userId = user.id
        val postDto = postService.createPost(createPostRequest, userId)
        return ResponseEntity.ok().body(postDto)
    }

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

    @PutMapping("/{postId}")
    fun updatePost(@AuthenticationPrincipal user: UserPrincipal, @PathVariable postId: Long,
                   @RequestBody updatePostRequest: PostUpdateDto
    ): ResponseEntity<Post> {
        val userId = user.id
        val postDto = postService.updatePost(postId, updatePostRequest, userId)
        return ResponseEntity.ok().body(postDto)
    }

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

    @DeleteMapping("/{postId}")
    fun deletePost(@AuthenticationPrincipal user: UserPrincipal, @PathVariable postId: Long): ResponseEntity<Void> {
        val userId = user.id
        postService.deletePost(postId, userId)
        return ResponseEntity.noContent().build()
    }
}

토큰인증인 @RequestHeader("Authorization") token: String을 빼고  @AuthenticationPrincipal user: UserPrincipal을 넣고, val post = postService.createPost(createPostRequest, token) 여기의 token 대신 userid를 넣는다

 

 

서비스도 

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

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

이렇게 바꾼다.

쉽게 고치기 위해서 디버그를 돌린뒤 에러코드를 찾아서 어디가 문제인지 보자,  

 

e: file:///C:/Users/asdf/Desktop/demo/src/main/kotlin/com/example/demo/controller/PostController.kt:21:65 Type mismatch: inferred type is String but Long was expected

 

이건 String 타입의 값을 Long 타입이 필요한 곳에 사용하려고 해서 타입 불일치 오류가 발생한 것으로 보입니다.
이 오류는 PostController의 21번째 줄에서 발생했다고 한다.

val postDto = postService.createPost(createPostRequest, userId.toString())

 

이부분인데, 웃긴게 빨간줄 떠서 알트 엔터 했더니 이렇게 바꿔놓고서 이러는거다. 아니 그럼 왜?? 어쨋든

userId이것만 남기고 뒤의 tostring는 지운다. 그리고 최종적으로...

 

package com.example.demo.controller

import com.example.demo.dto.PostCreateDto
import com.example.demo.dto.PostDto
import com.example.demo.dto.PostUpdateDto
import com.example.demo.security.UserPrincipal
import com.example.demo.service.PostService
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.*

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

    @PostMapping
    fun createPost(@AuthenticationPrincipal user: UserPrincipal,
                   @RequestBody createPostRequest: PostCreateDto): ResponseEntity<PostDto> {
        val userId = user.id
        val postDto = postService.createPost(createPostRequest, userId)
        return ResponseEntity.ok().body(postDto)
    }

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

    @PutMapping("/{postId}")
    fun updatePost(@AuthenticationPrincipal user: UserPrincipal, @PathVariable postId: Long,
                   @RequestBody updatePostRequest: PostUpdateDto
    ): ResponseEntity<PostDto> {
        val userId = user.id
        val postDto = postService.updatePost(postId, updatePostRequest, userId)
        return ResponseEntity.ok().body(postDto)
    }

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

    @DeleteMapping("/{postId}")
    fun deletePost(@AuthenticationPrincipal user: UserPrincipal, @PathVariable postId: Long): ResponseEntity<Void> {
        val userId = user.id
        postService.deletePost(postId, userId)
        return ResponseEntity.noContent().build()
    }
}

 

 

package com.example.demo.service

import com.example.demo.dto.PostCreateDto
import com.example.demo.dto.PostDto
import com.example.demo.dto.PostUpdateDto
import com.example.demo.model.Post
import com.example.demo.repository.MemberRepository
import com.example.demo.repository.PostRepository
import jakarta.transaction.Transactional
import org.springframework.stereotype.Service

@Service
class PostServiceImpl(private val postRepository: PostRepository, private val memberRepository: MemberRepository) : PostService {

    @Transactional
    override fun createPost(createPostRequest: PostCreateDto, userId: Long): PostDto {
        // 사용자 조회
        val member = memberRepository.findById(userId)
            .orElseThrow { IllegalArgumentException("유효하지 않은 사용자입니다.") }

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

        // 게시글 저장
        val savedPost = postRepository.save(post)

        // 저장된 Post를 PostDto로 변환 후 반환
        return PostDto.from(savedPost)
    }

    override fun getPost(postId: Long): PostDto {
        val post = postRepository.findById(postId)
            .orElseThrow { IllegalArgumentException("해당 id의 게시글이 존재하지 않습니다.") }

        return PostDto.from(post)
    }

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

    override fun updatePost(postId: Long, updatePostRequest: PostUpdateDto, userId: Long): PostDto {
        // 게시글 조회
        val post = postRepository.findById(postId)
            .orElseThrow { IllegalArgumentException("해당 id의 게시글이 존재하지 않습니다.") }

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

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

        // 수정된 게시글 저장
        val updatedPost = postRepository.save(post)

        // 저장된 Post를 PostDto로 변환 후 반환
        return PostDto.from(updatedPost)
    }


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

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

        // 게시글 삭제
        postRepository.deleteById(postId)
    }
}

이렇게 바꿔놓으면 된다.

다음에는 게시글과 댓글을 연동시켜서 게시글을 조회시 댓글들을 볼수 있도록 해야겠다.