SpringBoot RestAPI 좋아요 기능 구현 ( UI 는 없음 )
이슈로 좋아요 기능 구현 역할
환경 설정
목표 ) 1개의 게시글을 1명의 유저가 좋아요를 하는 기능을 개발
IDE : IntelliJ 2023
Language : Java 11
SpringBoot : 2.7.7
외 내용 Gradle 참고!
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.7'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'teamproject'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.projectlombok:lombok:1.18.22'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// Security
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-test'
// JWT
implementation 'io.jsonwebtoken:jjwt:0.9.1'
// Swagger
implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: '3.0.0'
implementation 'io.springfox:springfox-boot-starter:3.0.0'
}
tasks.named('test') {
useJUnitPlatform()
}
이번 프로젝트 이슈 중 하나 좋아요 기능을 맡게 됨
위에 디렉토리 구조에서 중요하게 볼 것은
Controller - Service - Repository 와 Entity(Like) DTO(LikeResponse) 다
좋아요 기능을 구현하기 위해 다음과 같은 로직을 거친다.
- 로그인을 실시한 유저가
- 해당 게시글에 좋아요 요청
- 요청한 좋아요가 존재하는지 검사
- 있으면 좋아요 취소 / 없으면 좋아요
엄청 단순하게 구현했다. 추후 더 좋은 방법이 떠오르면 리펙토링 할 예정이다.
소스코드
Like.java
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
@Table(name = "likes")
public class Like {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// private AlarmType alarm;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "crew_id")
private Crew crew;
}
like 는 mysql 예약어 이므로 꼭 @Table 어노테이션을 사용하여 테이블 이름을 달리 설정 해 줘야한다.
@OneToMany(mappedBy = "user")
private List<Like> likes = new ArrayList<>();
@OneToMany(mappedBy = "crew")
private List<Like> likes = new ArrayList<>();
- 1명이 여러 crew 를 좋아요 할 수 있고,
- 1개의 crew 가 여러 user 에게 좋아요를 받을 수 있다.
저장 되는 방식은
id | user_id | crew_id |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 1 | 4 |
4 | 2 | 2 |
- user_id 가 해당 게시글 (crew_id) 를 좋아요 함
- user_id 1인 사람이 2, 3, 4 게시글을 좋아요
- user_id 2 인 사람이 2 게시글을 좋아요
LikeController.java
@RestController
@RequestMapping("/api/v1/crews")
@RequiredArgsConstructor
public class LikeController {
private final LikeService likeService;
@PostMapping("/{crewId}/like")
public Response<LikeResponse> likeCrew(@PathVariable Long crewId, Authentication authentication){
LikeResponse goodResponse = likeService.goodCrew(crewId,authentication.getName());
return Response.success(goodResponse);
}
}
POST /api/v1/crews/{crewId}/like
1. 요청이 오면 authentication 에서 로그인한 정보에 대해서 캐치함 > 로그인 전제 하 진행
2. @PathVariable로 받은 crewId 값을 service 레이어로 넘김
LikeService.java
@Service
@RequiredArgsConstructor
public class LikeService {
private final LikeRepository likeRepository;
private final CrewRepository crewRepository;
private final UserRepository userRepository;
public LikeResponse goodCrew(Long crewId, String userName){
User user = userRepository.findByUserName(userName).orElseThrow(()->new AppException(ErrorCode.USERID_NOT_FOUND,ErrorCode.USERID_NOT_FOUND.getMessage()));
Crew crew = crewRepository.findById(crewId).orElseThrow(()->new AppException(ErrorCode.CREW_NOT_FOUND,ErrorCode.CREW_NOT_FOUND.getMessage()));
LikeResponse goodResponse = new LikeResponse();
if(user.getLikes().stream().anyMatch(like -> like.getCrew().equals(crew))){
likeRepository.deleteByUserAndCrew(user,crew);
goodResponse.setMessage("좋아요 취소");
} else {
likeRepository.save(Like.builder().crew(crew).user(user).build());
goodResponse.setMessage("좋아요 성공");
}
return goodResponse;
}
}
1. 서비스 레이어에서는 컨트롤러로 부터 받은 게시글의 id 와 로그인한 유저 정보를 가지고 repository에 있는지 확인
2. findBy ** 으로 해당 엔티티가 있는지 찾고 없으면 에러를 발생시킨다. (에러는 custom 이여서 선택)
3. User 엔티티의 OneToMany로 매핑된 Like (ArrayList) 를 stream 메소드를 통해 crew 값이 있는지 판단하여 로직수행
- 람다식 형식을 공부할 필요성을 느낌
allMatch() 모든 요소들이 매개값(Predicate)으로 주어진 조건을 만족하는지 조사
anyMatch() 최소한 한 개의 요소가 주어진 조건에 만족하는지 조사
noneMatch() 모든 요소들이 주어진 조건을 만족하지 않는지 조사
LikeRepository.java
public interface LikeRepository extends JpaRepository<Like, Long> {
void deleteByUserAndCrew(User user, Crew crew);
}
1. 삭제 부분에 대해서 해당 정보를 가진 Like 엔티티 삭제 ( 추후 Soft Delete 로 구현 )
실행 화면
확인할때 Autentication 을 사용하기 때문에 SpringSecurityContext에 값이 들어 올 수 있도록 설정을 해주자
ex) 위의 방식은 Jwt 토큰을 넘겨주는 방식을 사용함
팀 프로젝트가 순조롭게 진행되고 있다.. 순조로워서 더욱 불안한 감정이 든다..
하지만 중요한건 꺾이지 않는 마음
'2023년 > 멋쟁이사자처럼 팀프로젝트' 카테고리의 다른 글
[팀프로젝트] 비동기 방식 통신 + 스프링부트 RestController 활용한 웹 페이지 (0) | 2023.01.26 |
---|---|
[팀프로젝트] 깃허브 워크플로우 (2) | 2023.01.20 |
[팀프로젝트] Springboot 사용하면서 UI 화면은 어떻게 처리하면 좋을까? (1) | 2023.01.19 |
[팀프로젝트] SpringBoot + webSocket 으로 간단한 채팅창 만들기 (0) | 2023.01.19 |
[팀프로젝트] SpringSecurity + Jwt 적용 (0) | 2023.01.17 |