Skip to content

Commit 1f146de

Browse files
authored
[Feat] 카테고리 조회,등록,수정,삭제 (#226)
* work * feat/ 카테고리 crud * feat/ SecurityConfig에 권한 설정
1 parent 0a0fa94 commit 1f146de

File tree

8 files changed

+386
-4
lines changed

8 files changed

+386
-4
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
package com.back.domain.product.category.controller;
2+
3+
import com.back.domain.product.category.dto.request.CategoryRequest;
4+
import com.back.domain.product.category.dto.response.CategoryResponse;
5+
import com.back.domain.product.category.service.CategoryService;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.Parameter;
8+
import io.swagger.v3.oas.annotations.media.Content;
9+
import io.swagger.v3.oas.annotations.media.Schema;
10+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
11+
import io.swagger.v3.oas.annotations.tags.Tag;
12+
import jakarta.validation.Valid;
13+
import lombok.RequiredArgsConstructor;
14+
import org.springframework.web.bind.annotation.*;
15+
16+
import java.util.List;
17+
18+
@RestController
19+
@RequiredArgsConstructor
20+
@RequestMapping("/api/categories")
21+
@Tag(name = "카테고리", description = "카테고리 관련 API")
22+
public class CategoryController {
23+
private final CategoryService categoryService;
24+
25+
/** 전체 카테고리 조회 */
26+
@GetMapping
27+
@Operation(
28+
summary = "전체 카테고리 조회",
29+
description = "상위/하위 카테고리 전체 조회",
30+
responses = {
31+
@ApiResponse(
32+
responseCode = "200",
33+
description = "카테고리 조회 성공",
34+
content = @Content(
35+
mediaType = "application/json",
36+
schema = @Schema(
37+
example = """
38+
[
39+
{
40+
"id": 1,
41+
"categoryName": "스티커",
42+
"subCategories": [
43+
{
44+
"id": 2,
45+
"categoryName": "씰스티커",
46+
"subCategories": []
47+
},
48+
{
49+
"id": 3,
50+
"categoryName": "자석스티커",
51+
"subCategories": []
52+
}
53+
]
54+
}
55+
]
56+
"""
57+
)
58+
)
59+
)
60+
}
61+
)
62+
public List<CategoryResponse> getAllCategories() {
63+
return categoryService.getAllCategories();
64+
}
65+
66+
/** 카테고리 등록 */
67+
@PostMapping
68+
@Operation(
69+
summary = "카테고리 등록",
70+
responses = {
71+
@ApiResponse(
72+
responseCode = "200",
73+
description = "카테고리 등록 성공",
74+
content = @Content(
75+
mediaType = "application/json",
76+
schema = @Schema(
77+
example = """
78+
{
79+
"id": 1,
80+
"categoryName": "스티커",
81+
"subCategories": []
82+
}
83+
"""
84+
)
85+
)
86+
),
87+
@ApiResponse(
88+
responseCode = "400",
89+
description = "중복 카테고리 또는 잘못된 요청",
90+
content = @Content(
91+
mediaType = "application/json",
92+
schema = @Schema(
93+
example = """
94+
{
95+
"resultCode": "400",
96+
"msg": "동일한 이름의 카테고리가 이미 존재합니다.",
97+
"data": null
98+
}
99+
"""
100+
)
101+
)
102+
)
103+
}
104+
)
105+
public CategoryResponse createCategory(@RequestBody @Valid CategoryRequest request) {
106+
return categoryService.createCategory(request);
107+
}
108+
109+
/** 카테고리 수정 */
110+
@PutMapping("/{id}")
111+
@Operation(
112+
summary = "카테고리 수정",
113+
parameters = {
114+
@Parameter(name = "id", description = "수정할 카테고리 ID", required = true)
115+
},
116+
responses = {
117+
@ApiResponse(
118+
responseCode = "200",
119+
description = "카테고리 수정 성공",
120+
content = @Content(
121+
mediaType = "application/json",
122+
schema = @Schema(
123+
example = """
124+
{
125+
"id": 1,
126+
"categoryName": "스티커",
127+
"subCategories": []
128+
}
129+
"""
130+
)
131+
)
132+
),
133+
@ApiResponse(
134+
responseCode = "400",
135+
description = "중복 카테고리 이름",
136+
content = @Content(
137+
mediaType = "application/json",
138+
schema = @Schema(
139+
example = """
140+
{
141+
"resultCode": "400",
142+
"msg": "동일한 이름의 카테고리가 이미 존재합니다.",
143+
"data": null
144+
}
145+
"""
146+
)
147+
)
148+
),
149+
@ApiResponse(
150+
responseCode = "404",
151+
description = "수정할 카테고리 없음",
152+
content = @Content(
153+
mediaType = "application/json",
154+
schema = @Schema(
155+
example = """
156+
{
157+
"resultCode": "404",
158+
"msg": "카테고리를 찾을 수 없습니다.",
159+
"data": null
160+
}
161+
"""
162+
)
163+
)
164+
)
165+
}
166+
)
167+
public CategoryResponse updateCategory(@PathVariable Long id, @RequestBody @Valid CategoryRequest request) {
168+
return categoryService.updateCategory(id, request);
169+
}
170+
171+
/** 카테고리 삭제 */
172+
@DeleteMapping("/{id}")
173+
@Operation(
174+
summary = "카테고리 삭제",
175+
parameters = {
176+
@Parameter(name = "id", description = "삭제할 카테고리 ID", required = true)
177+
},
178+
responses = {
179+
@ApiResponse(
180+
responseCode = "200",
181+
description = "카테고리 삭제 성공"
182+
),
183+
@ApiResponse(
184+
responseCode = "400",
185+
description = "하위 카테고리 또는 상품 존재로 삭제 불가",
186+
content = @Content(
187+
mediaType = "application/json",
188+
schema = @Schema(
189+
example = """
190+
{
191+
"resultCode": "400",
192+
"msg": "하위 카테고리가 존재하여 삭제할 수 없습니다.",
193+
"data": null
194+
}
195+
"""
196+
)
197+
)
198+
),
199+
@ApiResponse(
200+
responseCode = "404",
201+
description = "삭제할 카테고리 없음",
202+
content = @Content(
203+
mediaType = "application/json",
204+
schema = @Schema(
205+
example = """
206+
{
207+
"resultCode": "404",
208+
"msg": "카테고리를 찾을 수 없습니다.",
209+
"data": null
210+
}
211+
"""
212+
)
213+
)
214+
)
215+
}
216+
)
217+
public void deleteCategory(@PathVariable Long id) {
218+
categoryService.deleteCategory(id);
219+
}
220+
221+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.back.domain.product.category.dto.request;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
5+
/**
6+
* 카테고리 등록, 수정용 DTO
7+
*/
8+
public record CategoryRequest(
9+
@NotBlank(message = "카테고리 이름은 필수입니다.")
10+
String categoryName,
11+
12+
Long parentId // null이면 상위 카테고리, null이 아니면 해당 parentId를 상위 카테고리로 가지는 하위 카테고리
13+
) {
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.back.domain.product.category.dto.response;
2+
3+
import java.util.List;
4+
5+
/**
6+
* 카테고리 조회용 DTO
7+
*/
8+
public record CategoryResponse(
9+
Long id,
10+
String categoryName,
11+
List<CategoryResponse> subCategories // 하위 카테고리 리스트
12+
) {
13+
}

src/main/java/com/back/domain/product/category/entity/Category.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import jakarta.persistence.*;
66
import lombok.*;
77

8+
import java.util.ArrayList;
89
import java.util.List;
910

1011
@Entity
@@ -26,8 +27,8 @@ public class Category extends BaseEntity {
2627

2728
// 하위 카테고리
2829
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true) // 상위 카테고리 삭제 시 하위 카테고리들도 삭제
29-
private List<Category> subCategories; // 하위 카테고리 리스트
30+
private List<Category> subCategories = new ArrayList<>(); // 하위 카테고리 리스트
3031

3132
@OneToMany(mappedBy = "category")
32-
private List<Product> products; // 해당 카테고리에 속한 상품들
33+
private List<Product> products = new ArrayList<>(); // 해당 카테고리에 속한 상품들
3334
}

src/main/java/com/back/domain/product/category/repository/CategoryRepository.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
77

88
public interface CategoryRepository extends JpaRepository<Category, Long> {
99
/**
10-
* parend_id가 상위 카테고리인(해당 상위 카테고리를 부모로 가진) 하위 카테고리들을 찾는 메서드
10+
* parend_id(상위 카테고리)가 parentId인 하위 카테고리들 조회
1111
* @param parentId 상위 카테고리 ID
1212
* @return 하위 카테고리 리스트
1313
*/
1414
List<Category> findAllByParentId(Long parentId);
15+
16+
/** 상위 카테고리들 조회 */
17+
List<Category> findAllByParentIdIsNull();
18+
/** 카테고리 생성/수정 시 중복 방지용 */
19+
boolean existsByCategoryNameAndParent(String categoryName, Category parent);
1520
}

0 commit comments

Comments
 (0)