댓글 작성 API에 게시글에 대한 좋아요를 넣어야 한다.
package com.example.demo.model
import jakarta.persistence.*
@Entity
@Table(name = "PostLike")
class Like(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
@ManyToOne
val member: Member,
@ManyToOne
val post: Post
)
이렇게 like엔티티를 생성했는데, like는 데이터베이스 테이블의 이름으로 쓸수 없다고 하길래 @Table(name = "PostLike")를 넣어서 이름을 바꿨다.
interface LikeService {
fun addLike(memberId: Long, postId: Long)
}
data class CommentCreateDto(
val postId: Long,
val nickname: String,
val content: String,
val isLike: Boolean // 좋아요를 눌렀는지 여부를 표시하는 필드
)
이렇게 각각 작성하고
package com.example.demo.service
import com.example.demo.model.Like
import com.example.demo.repository.LikeRepository
import com.example.demo.repository.MemberRepository
import com.example.demo.repository.PostRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@Service
class LikeServiceImpl(
private val postRepository: PostRepository,
private val memberRepository: MemberRepository,
private val likeRepository: LikeRepository
) : LikeService {
@Transactional
override fun addLike(memberId: Long, postId: Long) {
val member = memberRepository.findById(memberId).orElseThrow {
IllegalArgumentException("해당하는 회원이 없습니다.")
}
val post = postRepository.findById(postId).orElseThrow {
IllegalArgumentException("해당하는 게시글이 없습니다.")
}
if (likeRepository.existsByMemberAndPost(member, post)) {
throw IllegalArgumentException("이미 좋아요를 누른 게시글입니다.")
}
val like = Like(member = member, post = post)
likeRepository.save(like)
}
}
package com.example.demo.service
import com.example.demo.dto.CommentCreateDto
import com.example.demo.dto.CommentDto
import com.example.demo.dto.CommentUpdateDto
import com.example.demo.model.Comment
import com.example.demo.repository.CommentRepository
import com.example.demo.repository.LikeRepository
import com.example.demo.repository.PostRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import com.example.demo.model.Like
import com.example.demo.repository.MemberRepository
@Service
class CommentServiceImpl(
private val commentRepository: CommentRepository,
private val postRepository: PostRepository,
private val likeRepository: LikeRepository,
private val memberRepository: MemberRepository
) : CommentService {
@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,
userId = userId,
nickname = dto.nickname,
content = dto.content
)
commentRepository.save(comment)
// 좋아요를 처리하는 로직
if (dto.isLike) {
val member = memberRepository.findById(userId).orElseThrow { IllegalArgumentException("Member not found") }
val post = postRepository.findById(dto.postId).orElseThrow { IllegalArgumentException("Post not found") }
val like = Like(member = member, post = post)
likeRepository.save(like)
}
return CommentDto.from(comment)
}
@Transactional
override fun updateComment(id: Long, userId: Long, dto: CommentUpdateDto): CommentDto {
val comment = commentRepository.findByIdAndUserId(id, userId)
.orElseThrow { IllegalArgumentException("Comment not found or not owned by user") }
comment.content = dto.content
return CommentDto.from(comment)
}
@Transactional(readOnly = true)
override fun getComment(id: Long): CommentDto {
val comment = commentRepository.findById(id)
.orElseThrow { IllegalArgumentException("Comment not found") }
return CommentDto.from(comment)
}
@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)
}
}
지대로 작동하긴 하는데, 문제는 좋아요가 댓글 작성에 통합되어 있어서 그런지 댓글 하나를 작성하고 나면 에러메시지를 띄운다는것이다. 그 이유는 이미 좋아요가 된 게시글입니다! 라는 이유였다.
if (likeRepository.existsByMemberAndPost(member, post)) {
throw IllegalArgumentException("이미 좋아요를 누른 게시글입니다.")
}를 없애서 해결!!
다음에 해야할건,
- [ ] 회원 가입 버튼을 누르기 전, 같은 닉네임이 존재하는지 "확인" 버튼을 눌러 먼저 유효성 검증부터 할 수 있도록 해보기
- [ ] (챌린지 과제) 데이터베이스에 비밀번호를 평문으로 저장하는 것이 아닌, 단방향 암호화 알고리즘을 이용하여 암호화 해서 저장하도록 하기
package com.example.demo.security
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
@Configuration
class PasswordEncoderConfig {
@Bean
fun passwordEncoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}
}
package com.example.demo.service
import com.example.demo.dto.LoginRequest
import com.example.demo.dto.LoginResponse
import com.example.demo.dto.SignUpRequest
import com.example.demo.jwt.JwtPlugin
import com.example.demo.model.Member
import com.example.demo.repository.MemberRepository
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
@Service
class MemberServiceImpl(
private val memberRepository: MemberRepository,
private val jwtPlugin: JwtPlugin,
private val passwordEncoder: PasswordEncoder// 이것과
): MemberService {
override fun signUp(signUpRequest: SignUpRequest): Member {
if (signUpRequest.nickName == signUpRequest.password) {
throw IllegalArgumentException("비밀번호는 닉네임과 같을 수 없습니다.")
}
if (signUpRequest.password != signUpRequest.passwordConfirm) {
throw IllegalArgumentException("비밀번호와 비밀번호 확인이 일치하지 않습니다.")
}
if (memberRepository.existsByNickName(signUpRequest.nickName)) {
throw IllegalArgumentException("이미 존재하는 닉네임입니다.")
}
val encodedPassword = passwordEncoder.encode(signUpRequest.password)//이거
val newMember = Member(nickName = signUpRequest.nickName, password = signUpRequest.password)
return memberRepository.save(newMember)
}
override fun login(loginRequest: LoginRequest): LoginResponse {
val member = memberRepository.findByNickName(loginRequest.nickName)
?: throw IllegalArgumentException("닉네임 또는 패스워드를 확인해주세요.")
if (member.password != loginRequest.password) {
throw IllegalArgumentException("닉네임 또는 패스워드를 확인해주세요.")
}
return LoginResponse(
accessToken = jwtPlugin.generateAccessToken(
subject = member.id.toString(), // 사용자의 ID를 subject로 사용
nickName = member.nickName // 사용자의 닉네임을 nickName으로 사용
)
)
}
override fun existsByNickName(nickname: String): Boolean {
return memberRepository.existsByNickName(nickname)//이것도 추가
}
}
interface MemberService {
fun signUp(signUpRequest: SignUpRequest): Member
fun login(loginRequest: LoginRequest): LoginResponse
fun existsByNickName(nickname: String): Boolean // 확인을 위해 추가
}
package com.example.demo.controller
import com.example.demo.dto.LoginRequest
import com.example.demo.dto.SignUpRequest
import com.example.demo.model.Member
import com.example.demo.service.MemberService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/api/members")
class MemberController(private val memberService: MemberService) {
@PostMapping("/signup")
fun signUp(@RequestBody signUpRequest: SignUpRequest): ResponseEntity<Member> {
val member = memberService.signUp(signUpRequest)
return ResponseEntity.ok(member)
}
@PostMapping("/login")
fun login(@RequestBody loginRequest: LoginRequest): ResponseEntity<Any> {
return try {
val loginResponse = memberService.login(loginRequest)
ResponseEntity.ok().body(loginResponse)
} catch (e: IllegalArgumentException) {
ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.message)
}
}
@GetMapping("/check-nickname")
fun checkNickname(@RequestParam nickname: String): ResponseEntity<String> {
return if (memberService.existsByNickName(nickname)) {
ResponseEntity.badRequest().body("닉네임이 중복됩니다.")
} else {
ResponseEntity.ok().body("사용 가능한 닉네임입니다.")
}
}// 회원 가입 버튼을 누르기 전, 같은 닉네임이 존재하는지 "확인" 버튼을 눌러 먼저 유효성
검증부터 할 수 있도록 해보기가 이것
}
이러면 될거다!