팀프로젝트
[카우치코딩] 팀프로젝트 5주차 회고
개발하는묭이
2022. 7. 5. 11:33
- [o] study CRUD
- [o] 회원가입 및 로그인 프론트와 연결해서 맞춰봄
- [o] study 필터링 API
- [o] queryDSL 세팅
queryDSL 세팅
querDSL 사용목적
- 문법이 sql문과 유사하다
- JPQL의 String으로 작성하는 sql문에 비해 queryDSL 문법은 자바를 사용하기 때문에 Compile 시점에 미리 에러를 잡아준다
- 전체 파일이 실행되기 전에 문제가 있으면 바로 알려준다
- 복잡하고 동적인 쿼리를 가능하게 해준다
QFile 생성
참고 - https://whitepro.tistory.com/446
https://jaime-note.tistory.com/67
- buid.gradle에 의존성 추가
// querydsl 추가
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
//querydsl 추가 시작
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
// querydsl 이 compileClassPath 를 상속하도록 설정
configurations {
compileOnly {
extendsFrom annotationProcessor
}
querydsl.extendsFrom compileClasspath
}
//querydsl 추가 끝
//querydsl 추가
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
id 'org.springframework.boot' version '2.7.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
//querydsl 추가
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
id 'java'
}
queryDSL 설정 순서
- queryDSL 플러그인 추가
- 라이브러리 dependency 추가
- queryDSL에서 사용할 경로 선언
- JPA 사용 여부와 사용할 경로 지정
- build시 사용할 sourceSet을 추가
- queryDSL이 compileClassPath를 상속하도록 설정
- queryDSL 컴파일시 사용할 옵션을 설정
compileQuerydsl 실행
- Reload Gradle Project 실행
- Gradle Tasks → compileQuerydsl 실행
- build/generated/querydsl 경로에 Entity들의 QClass가 생성되는지 확인할것
- 결과 화면
study 필터링 및 페이징 API 개발
- 인피니티 스크롤 구현을 위해 Pageable 인터페이스를 활용하여 페이징처리를 진행하였다
- org.springframework.data.domain.Pageable
- 페이징을 제공하는 인터페이스이다
- org.springframework.data.domain.Page
- • 페이징의 findAll() 의 기본적인 반환 메서드로 여러 반환 타입 중 하나이다.
- JpaRepository<>를 사용할때 findAll 메서드를 Pageable 인터페이스의 파림터로 넘기면 페이징을 사용할 수 있다
- 참고 - https://wonit.tistory.com/483
// 스터디 필터링 (조건검색)
@GetMapping("")
public Page<Study> studySearch( Pageable pageable, @RequestParam(value = "studyType", required = false ) String studyType,
@RequestParam( value = "studyDays", required = false) String studyDays, @RequestParam(value = "timeZone", required = false) String timeZone
, @RequestParam(value = "status",required = false) String status, @RequestParam(value = "studyName", required = false) String studyName) {
Page<Study> searchResult = studyService.findByAllCategory(pageable, studyType, studyDays, timeZone, status , studyName);
if (searchResult.equals(null)) {
throw new StudySearchNotFoundException();
}
return searchResult;
}
- 인테페이스
public interface StudyRepositoryCustom {
Page<Study> findAllBySearchOption(Pageable pageable, String studyType , String studyDays, String timeZone , String status, String studyName);
}
- 실행 결과
- localhost:8080/studies?page=0&size=4 ⇒ 첫번째 페이지 , 한페이지에 4개씩 보여주겠다
{
"content": [
{
"createdDate": "2022-06-26T11:53:25.498083",
"lastModifiedDate": "2022-06-26T12:10:04.585925",
"studyId": 73,
"studyType": "CS 지식",
"studyName": "CS와 면접 준비",
"studyDays": "미정",
"timeZone": "저녁 (18:00 - 24:00)",
"participants": 5,
"currentParticipants": 2,
"startDate": "2022-07-02 00:00:00",
"openChatUrl": "<https://open.kakao.com/>",
"studyIntroduce": "CS는 흔히 자료구조, 알고리즘, 컴퓨터구조, 운영체제, 네트워크, 데이터베이스와 같은 컴퓨터 기초지식들을 이야기한다.",
"studyGoal": "CS는 흔히 자료구조, 알고리즘, 컴퓨터구조, 운영체제, 네트워크, 데이터베이스와 같은 컴퓨터 기초지식들을 이야기한다. 위에있는 6개의 내용은 질문하면 대답이 가능하도록 공부를 해야한다.",
"status": "완료",
"endDate": "2022-10-02 00:00:00",
"likeCount": null,
"createUid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"likeStatus": null,
"studyMembers": [
{
"createdDate": "2022-06-26T11:53:25.517208",
"lastModifiedDate": "2022-06-26T11:53:25.517208",
"studyMemberId": 91,
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"member": {
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"displayName": "황유진",
"email": "cmk664488@gmail.com",
"blogUrl": "<https://velog.io/@trelawney>",
"githubUrl": "<https://github.com/ujtap0?tab=repositories>",
"photoUrl": "<https://lh3.googleusercontent.com/a/AATXAJy-nxiYfNUyNVazka8hszGGVnqO7sSKBX5TPs40=s96-c>",
"nickName": "8기-Front-황유진",
"introduce": "안녕하세요! 반갑습니다!",
"techStack": [
"Javascript",
"Html5",
"Css3",
"React",
"Redux"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"studyId": 73,
"role": "leader"
},
{
"createdDate": "2022-06-26T11:56:04.506977",
"lastModifiedDate": "2022-06-26T11:56:04.506977",
"studyMemberId": 92,
"uid": "I6mKo2Qg8CMOhJ92X3wgiAlVlIh2",
"member": {
"uid": "I6mKo2Qg8CMOhJ92X3wgiAlVlIh2",
"displayName": "Mimi Lee",
"email": "ivvvy.e@gmail.com",
"blogUrl": "<https://velog.io/@ivvvy>",
"githubUrl": "<https://github.com/i-vvvy>",
"photoUrl": "<https://lh3.googleusercontent.com/a-/AOh14GhjoXMjTcUs-VzIkr-0i8m6b91Exbi_C668gR6k=s96-c>",
"nickName": "8기-Front-이미미",
"introduce": " 프론트엔드 스터디 함께해요 : )",
"techStack": [
"Javascript",
"Html5",
"Css3"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"studyId": 73,
"role": "member"
}
],
"studyBlogs": [
{
"studyBlogId": 53,
"comment": "면접 준비 팁입니다! 참고해주세요",
"shareLink": "<https://mungto.tistory.com/517>",
"member": {
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"displayName": "황유진",
"email": "cmk664488@gmail.com",
"blogUrl": "<https://velog.io/@trelawney>",
"githubUrl": "<https://github.com/ujtap0?tab=repositories>",
"photoUrl": "<https://lh3.googleusercontent.com/a/AATXAJy-nxiYfNUyNVazka8hszGGVnqO7sSKBX5TPs40=s96-c>",
"nickName": "8기-Front-황유진",
"introduce": "안녕하세요! 반갑습니다!",
"techStack": [
"Javascript",
"Html5",
"Css3",
"React",
"Redux"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
}
}
],
"comments": [
{
"createdDate": "2022-06-27T08:08:14.754006",
"lastModifiedDate": "2022-06-27T08:08:14.754006",
"id": 145,
"member": {
"uid": "dy6dEPfvXEhG2lK0bgulLOIt2As1",
"displayName": "미영최",
"email": "goodlife1359@gmail.com",
"blogUrl": "<https://unique-wandflower-4cc.notion.site/d5b4447e7e1a4b79b5b886c89cd5dccc>",
"githubUrl": "<https://github.com/meeyoungchoi>",
"photoUrl": "<https://lh3.googleusercontent.com/a-/AOh14GiIXx0M7zDoRfnbVJU4nEuZhnt0p7xnmD1nsD2TAg=s96-c>",
"nickName": "8기-Back-최미영",
"introduce": "하이하이",
"techStack": [
"Javascript",
"NodeJs",
"NestJs",
"GraphQL",
"Go",
"Django"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"content": "화이팅"
},
{
"createdDate": "2022-06-26T11:53:46.049781",
"lastModifiedDate": "2022-06-26T11:53:46.049781",
"id": 120,
"member": {
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"displayName": "황유진",
"email": "cmk664488@gmail.com",
"blogUrl": "<https://velog.io/@trelawney>",
"githubUrl": "<https://github.com/ujtap0?tab=repositories>",
"photoUrl": "<https://lh3.googleusercontent.com/a/AATXAJy-nxiYfNUyNVazka8hszGGVnqO7sSKBX5TPs40=s96-c>",
"nickName": "8기-Front-황유진",
"introduce": "안녕하세요! 반갑습니다!",
"techStack": [
"Javascript",
"Html5",
"Css3",
"React",
"Redux"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"content": "많은 관심 부탁드립니다!"
},
{
"createdDate": "2022-06-26T11:55:59.303149",
"lastModifiedDate": "2022-06-26T11:55:59.303149",
"id": 121,
"member": {
"uid": "I6mKo2Qg8CMOhJ92X3wgiAlVlIh2",
"displayName": "Mimi Lee",
"email": "ivvvy.e@gmail.com",
"blogUrl": "<https://velog.io/@ivvvy>",
"githubUrl": "<https://github.com/i-vvvy>",
"photoUrl": "<https://lh3.googleusercontent.com/a-/AOh14GhjoXMjTcUs-VzIkr-0i8m6b91Exbi_C668gR6k=s96-c>",
"nickName": "8기-Front-이미미",
"introduce": " 프론트엔드 스터디 함께해요 : )",
"techStack": [
"Javascript",
"Html5",
"Css3"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"content": "스터디는 온라인으로 진행되나요?"
}
],
"studyLikes": [
{
"id": 154,
"member": {
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"displayName": "황유진",
"email": "cmk664488@gmail.com",
"blogUrl": "<https://velog.io/@trelawney>",
"githubUrl": "<https://github.com/ujtap0?tab=repositories>",
"photoUrl": "<https://lh3.googleusercontent.com/a/AATXAJy-nxiYfNUyNVazka8hszGGVnqO7sSKBX5TPs40=s96-c>",
"nickName": "8기-Front-황유진",
"introduce": "안녕하세요! 반갑습니다!",
"techStack": [
"Javascript",
"Html5",
"Css3",
"React",
"Redux"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"likeStatus": true
},
{
"id": 160,
"member": {
"uid": "dy6dEPfvXEhG2lK0bgulLOIt2As1",
"displayName": "미영최",
"email": "goodlife1359@gmail.com",
"blogUrl": "<https://unique-wandflower-4cc.notion.site/d5b4447e7e1a4b79b5b886c89cd5dccc>",
"githubUrl": "<https://github.com/meeyoungchoi>",
"photoUrl": "<https://lh3.googleusercontent.com/a-/AOh14GiIXx0M7zDoRfnbVJU4nEuZhnt0p7xnmD1nsD2TAg=s96-c>",
"nickName": "8기-Back-최미영",
"introduce": "하이하이",
"techStack": [
"Javascript",
"NodeJs",
"NestJs",
"GraphQL",
"Go",
"Django"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"likeStatus": true
}
]
},
{
"createdDate": "2022-06-27T02:20:00.81225",
"lastModifiedDate": "2022-06-27T07:48:17.020072",
"studyId": 78,
"studyType": "알고리즘/자료구조",
"studyName": "[알고리즘]스터디원 모집합니다",
"studyDays": "주중",
"timeZone": "저녁 (18:00 - 24:00)",
"participants": 10,
"currentParticipants": 1,
"startDate": "2022-07-11 00:00:00",
"openChatUrl": "<https://open.kakao.com/>",
"studyIntroduce": "스터디 소개",
"studyGoal": "스터디 목표",
"status": "완료",
"endDate": "2022-09-19 00:00:00",
"likeCount": null,
"createUid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"likeStatus": null,
"studyMembers": [
{
"createdDate": "2022-06-27T02:20:00.81718",
"lastModifiedDate": "2022-06-27T02:20:00.81718",
"studyMemberId": 100,
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"member": {
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"displayName": "황유진",
"email": "cmk664488@gmail.com",
"blogUrl": "<https://velog.io/@trelawney>",
"githubUrl": "<https://github.com/ujtap0?tab=repositories>",
"photoUrl": "<https://lh3.googleusercontent.com/a/AATXAJy-nxiYfNUyNVazka8hszGGVnqO7sSKBX5TPs40=s96-c>",
"nickName": "8기-Front-황유진",
"introduce": "안녕하세요! 반갑습니다!",
"techStack": [
"Javascript",
"Html5",
"Css3",
"React",
"Redux"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"studyId": 78,
"role": "leader"
}
],
"studyBlogs": [],
"comments": [],
"studyLikes": [
{
"id": 172,
"member": {
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"displayName": "황유진",
"email": "cmk664488@gmail.com",
"blogUrl": "<https://velog.io/@trelawney>",
"githubUrl": "<https://github.com/ujtap0?tab=repositories>",
"photoUrl": "<https://lh3.googleusercontent.com/a/AATXAJy-nxiYfNUyNVazka8hszGGVnqO7sSKBX5TPs40=s96-c>",
"nickName": "8기-Front-황유진",
"introduce": "안녕하세요! 반갑습니다!",
"techStack": [
"Javascript",
"Html5",
"Css3",
"React",
"Redux"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"likeStatus": true
},
{
"id": 173,
"member": {
"uid": "I6mKo2Qg8CMOhJ92X3wgiAlVlIh2",
"displayName": "Mimi Lee",
"email": "ivvvy.e@gmail.com",
"blogUrl": "<https://velog.io/@ivvvy>",
"githubUrl": "<https://github.com/i-vvvy>",
"photoUrl": "<https://lh3.googleusercontent.com/a-/AOh14GhjoXMjTcUs-VzIkr-0i8m6b91Exbi_C668gR6k=s96-c>",
"nickName": "8기-Front-이미미",
"introduce": " 프론트엔드 스터디 함께해요 : )",
"techStack": [
"Javascript",
"Html5",
"Css3"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"likeStatus": true
}
]
},
{
"createdDate": "2022-06-27T02:17:41.68386",
"lastModifiedDate": "2022-06-27T10:22:26.941598",
"studyId": 77,
"studyType": "모바일",
"studyName": "Flutter 스터디원 모집!",
"studyDays": "주중",
"timeZone": "저녁 (18:00 - 24:00)",
"participants": 5,
"currentParticipants": 1,
"startDate": "2022-06-29 00:00:00",
"openChatUrl": "<https://open.kakao.com/>",
"studyIntroduce": "스터디 소개",
"studyGoal": "스터디 목표",
"status": "완료",
"endDate": "2022-08-29 00:00:00",
"likeCount": null,
"createUid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"likeStatus": null,
"studyMembers": [
{
"createdDate": "2022-06-27T02:17:41.703765",
"lastModifiedDate": "2022-06-27T02:17:41.703765",
"studyMemberId": 99,
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"member": {
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"displayName": "황유진",
"email": "cmk664488@gmail.com",
"blogUrl": "<https://velog.io/@trelawney>",
"githubUrl": "<https://github.com/ujtap0?tab=repositories>",
"photoUrl": "<https://lh3.googleusercontent.com/a/AATXAJy-nxiYfNUyNVazka8hszGGVnqO7sSKBX5TPs40=s96-c>",
"nickName": "8기-Front-황유진",
"introduce": "안녕하세요! 반갑습니다!",
"techStack": [
"Javascript",
"Html5",
"Css3",
"React",
"Redux"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"studyId": 77,
"role": "leader"
}
],
"studyBlogs": [],
"comments": [],
"studyLikes": []
},
{
"createdDate": "2022-06-27T02:30:32.029231",
"lastModifiedDate": "2022-06-27T02:30:38.510607",
"studyId": 83,
"studyType": "CS 지식",
"studyName": "컴퓨터 네트워크 공부하실 분",
"studyDays": "미정",
"timeZone": "미정",
"participants": 6,
"currentParticipants": 1,
"startDate": "2022-07-11 00:00:00",
"openChatUrl": "<https://open.kakao.com/>",
"studyIntroduce": "test",
"studyGoal": "test",
"status": "완료",
"endDate": "2022-09-27 00:00:00",
"likeCount": null,
"createUid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"likeStatus": null,
"studyMembers": [
{
"createdDate": "2022-06-27T02:30:32.033731",
"lastModifiedDate": "2022-06-27T02:30:32.033731",
"studyMemberId": 105,
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"member": {
"uid": "TaXw94uJziYmXYyjBr8tNMvpb1v1",
"displayName": "황유진",
"email": "cmk664488@gmail.com",
"blogUrl": "<https://velog.io/@trelawney>",
"githubUrl": "<https://github.com/ujtap0?tab=repositories>",
"photoUrl": "<https://lh3.googleusercontent.com/a/AATXAJy-nxiYfNUyNVazka8hszGGVnqO7sSKBX5TPs40=s96-c>",
"nickName": "8기-Front-황유진",
"introduce": "안녕하세요! 반갑습니다!",
"techStack": [
"Javascript",
"Html5",
"Css3",
"React",
"Redux"
],
"enabled": false,
"authorities": null,
"accountNonExpired": false,
"credentialsNonExpired": false,
"username": null,
"password": null,
"accountNonLocked": false
},
"studyId": 83,
"role": "leader"
}
],
"studyBlogs": [],
"comments": [],
"studyLikes": []
}
],
"pageable": {
"sort": {
"empty": true,
"unsorted": true,
"sorted": false
},
"offset": 0,
"pageSize": 4,
"pageNumber": 0,
"paged": true,
"unpaged": false
},
"last": false,
"totalPages": 7,
"totalElements": 28,
"size": 4,
"number": 0,
"sort": {
"empty": true,
"unsorted": true,
"sorted": false
},
"first": true,
"numberOfElements": 4,
"empty": false
}
회원가입 및 로그인 프론트와 연결해서 테스트
- 진행하면서 발생했던 에러
구글 로그인 성공후 반환받은 토큰으로 우리 사이트에 로그인시 401예외가 발생한다
RequestUtil 클래스 16번 줄
//헤더값에 Authorization 값이 없거나 유효하지 않은 경우
if(header == null || !header.startsWith("Bearer ")) {
throw new CustomException(ErrorCode.MemberUnAuthorized);
}
헤더에 담긴 토큰이 형식이 잘못되 있거나 유효하지 않은 경우 일때
MemberSerivce클래스의 decodeToken 메서드 64번줄 - 헤더에서 토큰을 꺼내는 부분
// 헤더에서 토큰을 꺼낸다
public FirebaseToken decodeToken(String header) {
try {
String token = RequestUtil.getAuthorizationToken(header);
return firebaseAuth.verifyIdToken(token);
} catch (IllegalArgumentException | FirebaseAuthException e) {
log.debug("저희 사이트 로그인시 헤더에서 토큰을 꺼낼때 예외발생: {}", e.getMessage());
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED,
"{\\"code\\":\\"INVALID_TOKEN\\", \\"message\\":\\"" + e.getMessage() + "\\"}");
}
}
결론 토큰이 유효하지 않거나 형식이 잘못되어 구글에서 응답받은 토큰과 백으로 넘어온 토큰을 비교할때 일치하지 않아 예외가 발생 예외가 있는 상태에서 회원가입을 진행하면 백으로 넘어온 헤더에 담긴 토큰 형식이 올바르지 않거나 유효하지 않아 403 예외 발생
헤더 값을 검증하는 getAuthorizationToken 메서드에서 예외 발생
// 헤더값 검증
public static String getAuthorizationToken(String header) {
log.debug("incoming Authorization header: " + header);
//헤더값에 Authorization 값이 없거나 유효하지 않은 경우
if(header == null || !header.startsWith("Bearer ")) {
throw new CustomException(ErrorCode.MemberUnAuthorized);
}
// parts[0] : bearer, parts[1] : token
String[] parts = header.split(" ");
if(parts.length != 2) {
throw new CustomException(ErrorCode.MemberUnAuthorized);
}
//Token return
return parts[1];
}
study CRUD
- study Create
- 로그인된 사용자만 스터디를 개설할수 있다
- 따라서, authentication 객체를 사용하여 로그인 여부를 검사한다
- 스터디 시작일이 현재 날짜보다 늦은 경우 시작날짜가 될수 없다
- 날짜를 체크하기 위한 예외처리도 스터디를 데이터베이스에 저장하기 전에 검증한다
- 스터디가 디비에 저장되었다면 HttpStatus 201을 반환한다
@PostMapping("")
public ResponseEntity<StudyResponseDto> createStudy(Authentication authentication,
@RequestBody @Valid StudyRequestDto studyRequestDto, BindingResult result) {
if (result.hasErrors() || studyRequestDto.getStartDate().isBefore(LocalDateTime.now()) && studyRequestDto.getEndDate().isBefore(LocalDateTime.now())) {
throw new ParameterBadRequestException(result);
}
Member member = ((Member) authentication.getPrincipal());
StudyResponseDto responseDto = studyService.createStudy(studyRequestDto, member);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(responseDto);
}
- study Read
- // 스터디 단일 조회 @GetMapping("/{studyId}") public ResponseEntity<StudyRoleResponseDto> studyDetail( @PathVariable @Valid Long studyId, HttpServletRequest request) { StudyRoleResponseDto studyRoleResponseDto = null; String header = null; try { header = RequestUtil.getAuthorizationToken(request); } catch (CustomException e) { studyRoleResponseDto = studyService.studyDetail(studyId); } if (header != null) { studyRoleResponseDto = studyService.studyDetail(studyId, header); } return ResponseEntity.status(HttpStatus.OK).body(studyRoleResponseDto); }
- 로그인된 사용자가 스터디를 조회하는 경우 role이 필요하다
- 해당 스터디의 개설자인 경우 : leader
- 해당 스터디의 참여자인 경우 : member
- 참여자도 개설자도 아닌 경우 : general
- 컨트롤러에서는 로그인 된 상태인지 되지 않은 상태인지를 판단한다
- 헤더에 담겨있는 토큰을 사용하여 로그인 여부를 판단한다
- header가 null인 경우 로그인 되지 않은 상태이다
- header가 null이 아닌 경우 로그인 된 상태이다
- 스터디 조회시 서비스의 파라미터로 로그인된 사용자 정보도 같이 전달해 준다
- 사용자가 로그인을 하고 스터디를 조회할때 백엔드 처리흐름
1. 먼저 유저가 휴대폰 인증을 완료한 이후 인증완료 버튼을 누르게 되면 로그인을 의미하는 http요청이 서버로 들어온다.
2. 이때 서버에서는 Spring Security의 필터가 이 요청을 가로채 먼저 검증이 필요한 요청인지 확인한다.
3. 검증이 필요한 요청이라면 해당 유저의 로그인 정보를 검증하는 사용자 정의 필터인 JwtFilter가 실행된다.
4. JwtFilter에서 요청 헤더에 담긴 authorization 토큰을 검증하고 유저 정보를 확인하여 authentication객체를 생성한다.
5. 생성된 authentication객체를 controller에 매개변수로 넘겨준다.6. controller에서 받은 authetication객체에 getPrincipal() 메소드를 활용하면 인증된 유저 객체를 얻을 수 있다.
참고 - https://kih0902.tistory.com/59
- study Update
// 스터디 수정 (단일 수정)
@PatchMapping("/{studyId}")
public ResponseEntity<StudyResponseDetailDto> studyUpdate(Authentication authentication, @PathVariable Long studyId ,@RequestBody @Valid StudyRequestDto studyRequestDto, BindingResult result) {
if (result.hasErrors()) {
throw new ParameterBadRequestException(result);
}
Member member = ((Member) authentication.getPrincipal());
Study updated = studyService.studyUpdate(studyId, studyRequestDto, member);
StudyResponseDetailDto studyResponseDetailDto = studyService.toDto(updated);
return ResponseEntity.status(HttpStatus.OK).body(studyResponseDetailDto);
}