Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

강원대 BE_고승현_6주차 과제_Step2 #250

Open
wants to merge 26 commits into
base: chris0825
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e5d6d10
feat: init last week codes
chris0825 Jul 29, 2024
cb47747
docs: improve code readability
chris0825 Jul 29, 2024
cf275b2
fix: change codes
chris0825 Jul 31, 2024
035dfa0
Merge branch 'chris0825' into step1
chris0825 Jul 31, 2024
a4c27e6
docs: add step1 function require
chris0825 Jul 31, 2024
6865be1
docs: add step1 function require
chris0825 Jul 31, 2024
a3f9d1e
Merge chris0825/step1 to step1
chris0825 Jul 31, 2024
3765016
Merge branch 'step1' of https://github.com/chris0825/spring-gift-poin…
chris0825 Jul 31, 2024
b71960f
docs: add step2 function require
chris0825 Jul 31, 2024
bdc1b14
fix: modify the administrator pages
chris0825 Aug 1, 2024
12bd8f9
build: add shell scripting
chris0825 Aug 1, 2024
7d87623
teat: temporary deletion of tests for deployment
chris0825 Aug 1, 2024
fa3009f
build: modify build path
chris0825 Aug 1, 2024
7e59917
fix: resolve confilct
chris0825 Aug 1, 2024
b7dec42
fix: modify interceptors path
chris0825 Aug 1, 2024
cda9546
fix: resolve CORS
chris0825 Aug 1, 2024
3e0fa59
fix: modify kakao login logic
chris0825 Aug 1, 2024
fda949c
fix: modify properties
chris0825 Aug 1, 2024
0b47e2b
fix(kakaoAPI): change redirec URL
chris0825 Aug 1, 2024
c5d188a
fix(kakaoAPI): change redirec URL
chris0825 Aug 1, 2024
e2f5bc0
fix: resolve cors policy
chris0825 Aug 1, 2024
3063e65
fix(kakaoAPI): change redirec URL
chris0825 Aug 1, 2024
550fd90
delete cors
chris0825 Aug 1, 2024
8f95c08
fix: cors
chris0825 Aug 1, 2024
b80420d
fix(kakaoAPI): change redirec URL
chris0825 Aug 1, 2024
2910970
fix(kakaoAPI): change redirec URL
chris0825 Aug 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 201 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,210 @@
## 목차
[* 코드 소개](#코드-소개)<br>
[* 0 단계 - 기본 코드 준비](#-0-단계----기본-코드-준비)<br>
[* 1 단계 - API 명세](#-1-단계----api-명세)<br>
[* 2 단계 - 배포하기](#-2-단계----배포하기)<br>

## 코드 소개
카카오 선물하기의 상품을 관리하는 서비스를 구현해본다

## < 0 단계 > - 기본 코드 준비
### [ 기능 요구 사항 ]
- [spring-gift-order](https://github.com/chris0825/spring-gift-order.git)의 코드를 옮겨온다.
- [spring-gift-order](https://github.com/chris0825/spring-gift-order.git)의 코드를 옮겨온다.

## < 1 단계 > - API 명세
### [ 기능 요구 사항 ]
> 작성한 API 문서를 기반으로 팀 내에서 지금까지 만든 API를 검토하고 통일하여 변경 사항을 반영한다.
- 팀 내에서 일관된 기준을 정하여 API 명세를 결정한다.
- 때로는 클라이언트의 편의를 위해 API 명세를 결정하는 것이 좋다.

### [ 연동 API 명세 ]
<details>
<summary> 로그인 API </summary>

### 카카오 로그인 및 회원 가입 (Authorize code & access token)
- request
> GET /kakao/login HTTP/1.1
- rseponse
```json
{
"token": "String"
}
```
### 로그인 이후 HTTP 헤더에 로그인 시 발급된 토큰을 포함한 인증 헤더가 존재하여야 한다.
> Authorization: Bearer {token}
</details>
<details>
<summary> 카테고리 API </summary>

### 카테고리 목록 조회
- request
> GET /api/categories HTTP/1.1
- response
```json
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": Long,
"name": "String",
"color": "String",
"imageUrl": "String",
"description": "String"
}
]
```
</details>
<details>
<summary> 상품 API </summary>

### 상품 상세 조회
- request
> GET /api/products/{productId} HTTP/1.1
- response
```json
HTTP/1.1 200 OK
Content-Type: application/json
{
"name": "String",
"price": "int",
"imageUrl": "String",
"categoryId": "Long"
}
```
### 전체 상품 목록 조회
- request
> GET /api/products?page=0&size=10&sort=name,asc&categoryId=1 HTTP/1.1
- response
```json
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": Long,
"name": "String",
"price": "int",
"imageUrl": "String",
"categoryId": "Long"
}
]
```
</details>
<details>
<summary> 옵션 API </summary>

### 상품 하위 옵션 조회
- request
> GET /api/products/{productId}/options HTTP/1.1
- response
```json
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": "Long",
"name": "String",
"quantity": "int"
}
]
```
</details>
<details>
<summary> 위시 API </summary>

### 위시 상품 추가
- request
```json
POST /api/wishes HTTP/1.1
Content-Type: application/json
Authorization: Bearer {token}
{
"productId": "Long"
}
```
- response
```json
HTTP/1.1 201 CREATED
Content-Type: application/json
{
"productId": "Long"
}
```
### 위시 상품 삭제
- request
> DELETE /api/wishes/{productId} HTTP/1.1
- response
```json
HTTP/1.1 204 NO_CONTENT
```
### 위시 리스트 조회
- request
> GET /api/wishes?page=0&size=10&sort=createdDate,desc HTTP/1.1
- response
```json
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": "Long",
"product": {
"id": "Long",
"name": "String",
"price": "int",
"imageUrl": "String"
}
}
]
```
</details>
<details>
<summary> 주문 API </summary>
### 주문하기
- request
```json
POST /api/orders HTTP/1.1
Content-Type: application/json
Authorization: Bearer {token}
{
"optionId": "Long",
"quantity": "int",
"message": "String"
}
```
- response
```json
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "Long",
"optionId": "Long",
"quantity": "int",
"orderDateTime": "YYYY-MM-DDTHH:MM:SS",
"message": "String"
}
```
### 주문 목록 조회
- request
> GET /api/orders?page=0&size=10&sort=orderDateTime,desc HTTP/1.1
- response
```json
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": "Long",
"optionId": "Long",
"quantity": "int",
"orderDateTime": "YYYY-MM-DDTHH:MM:SS",
"message": "String"
}
]
```
</details>

## < 2 단계 > - 배포하기
### [ 기능 요구 사항 ]
> 지금까지 만든 선물하기 서비스를 배포하고 클라이언트와 연동할 수 있어야 한다.
- 지속적인 배포를 위한 배포 스크립트를 작성한다.
- 클라이언트와 API 연동 시 발생하는 보안 문제에 대응한다.
- 서버와 클라이언트의 Origin이 달라 요청을 처리할 수 없는 경우를 해결한다.
- HTTPS는 필수는 아니지만 팀 내에서 논의하고 필요한 경우 적용한다.
20 changes: 20 additions & 0 deletions scripts/distribution.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash
BUILD_PATH=$(ls /home/ubuntu/spring-gift-point/build/libs/*.jar)
JAR_NAME=$(basename $BUILD_PATH)

CURRENT_PID=$(pgrep -f $JAR_NAME)

if [ -z $CURRENT_PID ]
then
sleep 1
else
kill -15 $CURRENT_PID
sleep 5
fi

DEPLOY_PATH=/home/ubuntu/spring-gift-point/
cp $BUILD_PATH $DEPLOY_PATH
cd $DEPLOY_PATH

DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME
nohup java -jar $DEPLOY_JAR > /dev/null 2> /dev/null < /dev/null &
14 changes: 13 additions & 1 deletion src/main/java/gift/product/config/WebMvcConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import gift.product.intercepter.AuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

Expand All @@ -16,7 +17,18 @@ public class WebMvcConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/kakao/**", "/api/member/**");
.excludePathPatterns(
"/api/kakao/**",
"/api/member/**",
"/api/products/**",
"/api/categories/**");
}

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("*")
.allowCredentials(true);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package gift.product.controller;

import gift.product.dto.CategoryDTO;
import gift.product.model.Category;
import gift.product.service.CategoryService;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -18,7 +17,7 @@
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/admin/category")
@RequestMapping("/admin/categories")
public class AdminCategoryController {

private final CategoryService categoryService;
Expand All @@ -31,7 +30,7 @@ public AdminCategoryController(CategoryService categoryService) {
@GetMapping
public String category(Model model, Pageable pageable) {
model.addAttribute("categoryList", categoryService.findAllCategory(pageable));
return "category";
return "category-management";
}

@GetMapping("/register")
Expand All @@ -42,15 +41,18 @@ public String registerCategoryForm(Model model) {
}

@PostMapping
public String registerCategory(@Valid @ModelAttribute CategoryDTO categoryDTO, BindingResult bindingResult, Model model) {
public String registerCategory(
@Valid @ModelAttribute CategoryDTO categoryDTO,
BindingResult bindingResult,
Model model) {
System.out.println("[AdminCategoryController] registerCategory()");
if(bindingResult.hasErrors()) {
System.out.println("[AdminCategoryController] registerCategory(): validation error: " + bindingResult.getAllErrors() + ", categoryDTO: " + categoryDTO.getName());
model.addAttribute("categoryDTO", categoryDTO);
return "category-form";
}
categoryService.registerCategory(categoryDTO.convertToDomain());
return "redirect:/admin/category";
return "redirect:/admin/categories";
}

@GetMapping("/{id}")
Expand All @@ -66,7 +68,7 @@ public String updateCategory(@PathVariable Long id, @Valid @ModelAttribute Categ
if(bindingResult.hasErrors()) {
model.addAttribute("categoryDTO", categoryDTO);
}
categoryService.updateCategory(new Category(id, categoryDTO.getName()));
categoryService.updateCategory(categoryDTO.convertToDomain(id));
return "redirect:/admin/category";
}

Expand Down
33 changes: 27 additions & 6 deletions src/main/java/gift/product/controller/AdminOptionController.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -17,7 +18,7 @@
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/admin/product/{productId}/option")
@RequestMapping("/admin/products/{productId}/options")
public class AdminOptionController {

private final OptionService optionService;
Expand Down Expand Up @@ -48,32 +49,52 @@ public String showOptionRegisterForm(@PathVariable Long productId, Model model)
}

@PostMapping
public String registerOption(@PathVariable Long productId, @Valid @ModelAttribute OptionDTO optionDTO, BindingResult bindingResult, Model model) {
public String registerOption(
@PathVariable Long productId,
@Valid @ModelAttribute OptionDTO optionDTO,
BindingResult bindingResult,
Model model) {
System.out.println("[AdminOptionController] registerOption()");
if (bindingResult.hasErrors()) {
System.out.println("registerOption(): validation error: " + bindingResult.getAllErrors());
model.addAttribute("option", optionDTO);
return "product-option-form";
}
optionService.registerOption(productId, optionDTO);
return "redirect:/admin/product/" + productId + "/option";
return "redirect:/admin/products/" + productId + "/options";
}

@GetMapping("/{optionId}")
public String showOptionUpdateForm(@PathVariable Long productId, @PathVariable Long optionId, Model model) {
public String showOptionUpdateForm(
@PathVariable Long productId,
@PathVariable Long optionId,
Model model) {
System.out.println("[AdminOptionController] showOptionUpdateForm()");
model.addAttribute("option", optionService.findById(optionId));
return "product-option-update-form";
}

@PutMapping("/{optionId}")
public String updateOption(@PathVariable Long optionId, @PathVariable Long productId, @Valid @ModelAttribute OptionDTO optionDTO, BindingResult bindingResult, Model model) {
public String updateOption(
@PathVariable Long optionId,
@PathVariable Long productId,
@Valid @ModelAttribute OptionDTO optionDTO,
BindingResult bindingResult,
Model model) {
System.out.println("[AdminOptionController] updateOption()");
if(bindingResult.hasErrors()) {
model.addAttribute("option", optionDTO);
return "product-option-update-form";
}
optionService.updateOption(optionId, productId, optionDTO);
return "redirect:/admin/product/" + productId + "/option";
return "redirect:/admin/products/" + productId + "/options";
}

@DeleteMapping("/{optionId}")
public String deleteOption(@PathVariable Long optionId, @PathVariable Long productId) {
System.out.println("[AdminOptionController] deleteOption()");
optionService.deleteOption(optionId, productId);
return "redirect:/admin/products/" + productId + "/options";
}

}
Loading