Skip to content

Commit 50e08ba

Browse files
authored
Merge pull request #20 from Moongeul/feat/#19
[FEAT] 글쓰기+카테고리 API - 기록 공개, 카테고리 설정 구현
2 parents 89ec35f + b5f6160 commit 50e08ba

File tree

18 files changed

+280
-28
lines changed

18 files changed

+280
-28
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.moongeul.backend.api.category.controller;
2+
3+
import com.moongeul.backend.api.category.dto.CategoryCreateRequestDTO;
4+
import com.moongeul.backend.api.category.dto.CategoryListResponseDTO;
5+
import com.moongeul.backend.api.category.dto.CategoryResponseDTO;
6+
import com.moongeul.backend.api.category.service.CategoryService;
7+
import com.moongeul.backend.common.response.ApiResponse;
8+
import com.moongeul.backend.common.response.SuccessStatus;
9+
import io.swagger.v3.oas.annotations.Operation;
10+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
11+
import io.swagger.v3.oas.annotations.tags.Tag;
12+
import jakarta.validation.Valid;
13+
import lombok.RequiredArgsConstructor;
14+
import org.springframework.http.ResponseEntity;
15+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
16+
import org.springframework.security.core.userdetails.UserDetails;
17+
import org.springframework.web.bind.annotation.*;
18+
19+
@Tag(name = "Category", description = "Category(카테고리-기록) 관련 API 입니다.")
20+
@RestController
21+
@RequiredArgsConstructor
22+
@RequestMapping("/api/v2/category")
23+
public class CategoryController {
24+
25+
private final CategoryService categoryService;
26+
27+
@Operation(
28+
summary = "내 카테고리 전체 조회 API",
29+
description = "사용자의 카테고리를 가져오는 API 입니다."
30+
)
31+
@ApiResponses({
32+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "카테고리 전체 조회 성공"),
33+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "카테고리명은 필수입니다. (category)"),
34+
})
35+
@GetMapping
36+
public ResponseEntity<ApiResponse<CategoryListResponseDTO>> getCategory(@AuthenticationPrincipal UserDetails userDetails) {
37+
38+
CategoryListResponseDTO response = categoryService.getCategory(userDetails.getUsername());
39+
return ApiResponse.success(SuccessStatus.GET_CATEGORY_SUCCESS, response);
40+
}
41+
42+
@Operation(
43+
summary = "카테고리 생성 API",
44+
description = "기록에 대한 카테고리를 생성하는 API 입니다."
45+
)
46+
@ApiResponses({
47+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "카테고리 생성 성공"),
48+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "카테고리명은 필수입니다. (category)"),
49+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "해당 카테고리를 찾을 수 없습니다."),
50+
})
51+
@PostMapping("/create")
52+
public ResponseEntity<ApiResponse<CategoryResponseDTO>> createCategory(@AuthenticationPrincipal UserDetails userDetails,
53+
@Valid @RequestBody CategoryCreateRequestDTO categoryCreateRequestDTO) {
54+
55+
CategoryResponseDTO response = categoryService.createCategory(categoryCreateRequestDTO, userDetails.getUsername());
56+
return ApiResponse.success(SuccessStatus.CREATE_CATEGORY_SUCCESS, response);
57+
}
58+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.moongeul.backend.api.category.dto;
2+
3+
import com.moongeul.backend.api.member.entity.Member;
4+
import com.moongeul.backend.api.category.entity.Category;
5+
import jakarta.validation.constraints.NotBlank;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
11+
@Getter
12+
@Builder
13+
@NoArgsConstructor
14+
@AllArgsConstructor
15+
public class CategoryCreateRequestDTO {
16+
17+
@NotBlank(message = "카테고리명은 필수입니다")
18+
private String category; // 카테고리명
19+
20+
public Category toEntity(Member member){
21+
22+
return Category.builder()
23+
.title(this.category)
24+
.member(member)
25+
.build();
26+
}
27+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.moongeul.backend.api.category.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.util.List;
9+
10+
@Getter
11+
@Builder
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public class CategoryListResponseDTO {
15+
16+
private List<CategoryResponseDTO> categoryList; // 카테고리 리스트
17+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.moongeul.backend.api.category.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
@Getter
9+
@Builder
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class CategoryResponseDTO {
13+
14+
private Long categoryId; // 카테고리 번호
15+
private String title; // 카테고리 제목
16+
}

src/main/java/com/moongeul/backend/api/post/entity/Category.java renamed to src/main/java/com/moongeul/backend/api/category/entity/Category.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.moongeul.backend.api.post.entity;
1+
package com.moongeul.backend.api.category.entity;
22

33
import com.moongeul.backend.api.member.entity.Member;
44
import jakarta.persistence.*;
@@ -19,7 +19,7 @@ public class Category {
1919
@GeneratedValue(strategy = GenerationType.IDENTITY)
2020
private Long id; // 카테고리 id
2121

22-
private String title; // 카테고리 제목
22+
private String title; // 카테고리명
2323

2424
@ManyToOne(fetch = FetchType.LAZY)
2525
@JoinColumn(name = "member_id", nullable = false)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.moongeul.backend.api.category.repository;
2+
3+
4+
import com.moongeul.backend.api.member.entity.Member;
5+
import com.moongeul.backend.api.category.entity.Category;
6+
import org.springframework.data.jpa.repository.JpaRepository;
7+
8+
import java.util.List;
9+
import java.util.Optional;
10+
11+
public interface CategoryRepository extends JpaRepository<Category, Long> {
12+
13+
Optional<List<Category>> findByMember(Member member);
14+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.moongeul.backend.api.category.service;
2+
3+
import com.moongeul.backend.api.category.dto.CategoryCreateRequestDTO;
4+
import com.moongeul.backend.api.category.dto.CategoryListResponseDTO;
5+
import com.moongeul.backend.api.category.dto.CategoryResponseDTO;
6+
import com.moongeul.backend.api.member.entity.Member;
7+
import com.moongeul.backend.api.member.repository.MemberRepository;
8+
import com.moongeul.backend.api.category.entity.Category;
9+
import com.moongeul.backend.api.category.repository.CategoryRepository;
10+
import com.moongeul.backend.common.exception.NotFoundException;
11+
import com.moongeul.backend.common.response.ErrorStatus;
12+
import lombok.RequiredArgsConstructor;
13+
import lombok.extern.slf4j.Slf4j;
14+
import org.springframework.stereotype.Service;
15+
import org.springframework.transaction.annotation.Transactional;
16+
17+
import java.util.List;
18+
19+
@Slf4j
20+
@Service
21+
@RequiredArgsConstructor
22+
public class CategoryService {
23+
24+
private final MemberRepository memberRepository;
25+
private final CategoryRepository categoryRepository;
26+
27+
/* 카테고리 생성 */
28+
@Transactional
29+
public CategoryResponseDTO createCategory(CategoryCreateRequestDTO categoryCreateRequestDTO, String email){
30+
31+
Member member = getMemberByEmail(email);
32+
33+
Category newCategory = categoryCreateRequestDTO.toEntity(member);
34+
Category savedCategory = categoryRepository.save(newCategory);
35+
36+
return CategoryResponseDTO.builder()
37+
.categoryId(savedCategory.getId())
38+
.title(savedCategory.getTitle())
39+
.build();
40+
}
41+
42+
/* 내 카테고리 전체 조회 */
43+
@Transactional
44+
public CategoryListResponseDTO getCategory(String email){
45+
46+
Member member = getMemberByEmail(email);
47+
48+
List<Category> categoryList = categoryRepository.findByMember(member)
49+
.orElseThrow(() -> new NotFoundException(ErrorStatus.CATEGORY_NOTFOUND_EXCEPTION.getMessage()));
50+
51+
List<CategoryResponseDTO> categoryResponseDTOList = categoryList.stream()
52+
.map(category -> CategoryResponseDTO.builder()
53+
.categoryId(category.getId())
54+
.title(category.getTitle())
55+
.build())
56+
.toList();
57+
58+
return CategoryListResponseDTO.builder()
59+
.categoryList(categoryResponseDTOList)
60+
.build();
61+
}
62+
63+
// 사용자 정보 가져오기 메서드
64+
private Member getMemberByEmail(String email) {
65+
return memberRepository.findByEmail(email)
66+
.orElseThrow(() -> new NotFoundException(ErrorStatus.USER_NOTFOUND_EXCEPTION.getMessage()));
67+
}
68+
}

src/main/java/com/moongeul/backend/api/member/controller/MemberController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class MemberController {
2727

2828
@Operation(
2929
summary = "구글 로그인 API",
30-
description = "구글 인가코드을 통해 사용자의 정보를 등록 및 토큰 + 역할을 발급합니다. (ROLE -> 처음사용자 : ROLE_GUEST, 일반사용자 : ROLE_USER, 관리자 : ROLE_ADMIN)"
30+
description = "구글 인가코드을 통해 사용자의 정보를 등록 및 토큰 + 역할을 발급합니다. (ROLE -> 처음사용자 : GUEST, 일반사용자 : USER, 관리자 : ADMIN)"
3131
)
3232
@ApiResponses({
3333
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "로그인 성공"),
@@ -44,7 +44,7 @@ public ResponseEntity<ApiResponse<LoginResponseDTO>> loginWithGoogle(@Valid @Req
4444

4545
@Operation(
4646
summary = "카카오 로그인 API",
47-
description = "카카오 인가코드을 통해 사용자의 정보를 등록 및 토큰 + 역할을 발급합니다. (ROLE -> 처음사용자 : ROLE_GUEST, 일반사용자 : ROLE_USER, 관리자 : ROLE_ADMIN)"
47+
description = "카카오 인가코드을 통해 사용자의 정보를 등록 및 토큰 + 역할을 발급합니다. (ROLE -> 처음사용자 : GUEST, 일반사용자 : USER, 관리자 : ADMIN)"
4848
)
4949
@ApiResponses({
5050
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "로그인 성공"),

src/main/java/com/moongeul/backend/api/member/jwt/filter/JwtAuthenticationFilter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
4141
// Request Header에서 토큰 정보 추출
4242
private String resolveToken(HttpServletRequest request) {
4343
String bearerToken = request.getHeader("Authorization");
44-
log.info("Authorization Header Value: [{}]", bearerToken); // 💡 값 전체 출력 (로그 레벨 info 이상)
4544

4645
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer")) {
4746
return bearerToken.substring(7).trim();

src/main/java/com/moongeul/backend/api/post/controller/PostController.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@
1313
import org.springframework.http.ResponseEntity;
1414
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1515
import org.springframework.security.core.userdetails.UserDetails;
16-
import org.springframework.web.bind.annotation.PostMapping;
17-
import org.springframework.web.bind.annotation.RequestBody;
18-
import org.springframework.web.bind.annotation.RequestMapping;
19-
import org.springframework.web.bind.annotation.RestController;
16+
import org.springframework.web.bind.annotation.*;
2017

2118
@Tag(name = "Post", description = "Post(게시글) 관련 API 입니다.")
2219
@RestController
@@ -30,7 +27,8 @@ public class PostController {
3027
summary = "글쓰기 API",
3128
description = "기록(게시글)을 작성하는 글쓰기 API 입니다." +
3229
"<br>필수: isbn, readDate / 선택: rating(default = 5.0), page(default = 300), content, quotes" +
33-
"<br>선택 요소의 경우 입력되지 않았을 때 'null'로 전달 바랍니다."
30+
"<br>선택 요소의 경우 입력되지 않았을 때 'null'로 전달 바랍니다." +
31+
"<br><br>[enum] postVisibility -> 전체 공개 : PUBLIC, 팔로워 공개 : FOLLOWERS, 나만보기 : PRIVATE"
3432
)
3533
@ApiResponses({
3634
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "글쓰기 성공"),
@@ -39,7 +37,7 @@ public class PostController {
3937
})
4038
@PostMapping("/create")
4139
public ResponseEntity<ApiResponse<PostCreateResponseDTO>> createPost(@AuthenticationPrincipal UserDetails userDetails,
42-
@Valid @RequestBody PostCreateRequestDTO postCreateRequestDTO) {
40+
@Valid @RequestBody PostCreateRequestDTO postCreateRequestDTO) {
4341

4442
PostCreateResponseDTO response = postService.createPost(postCreateRequestDTO, userDetails.getUsername());
4543
return ApiResponse.success(SuccessStatus.CREATE_POST_SUCCESS, response);

0 commit comments

Comments
 (0)