Git Product home page Git Product logo

ourdusbe's Introduction

Ourdus Backend

Idus ์˜ Clone Project์ž…๋‹ˆ๋‹ค.

Member

๊น€์„œ์—ฐ ์šฐํ˜œ์ง„ ์ดํ™”๊ฒฝ
@ksy991018 @HJ-Woo @HwaGyeong

Docuemnt

โœ” ERD
image ERD Cloud

โœ” API document image API Document WIKI

๊ธฐ์ˆ  ์Šคํƒ

  • Java 11
  • Spring Boot
  • JPA
  • AWS EC2, Docker
  • Travis CI
  • AWS RDS (MySQL) - Prod
  • H2 Database - Dev

-> ์™œ ์ด๋Ÿฐ ๊ธฐ์ˆ  ์Šคํƒ์„ ์„ ํƒํ–ˆ๋Š”๊ฐ€?

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

(main)
.
โ”œโ”€ ourdus-spring(backend)
โ”‚  โ”œโ”€ src
โ”‚  โ”‚  โ”œโ”€ main
โ”‚  โ”‚  โ”‚  โ””โ”€ java.ourdus.ourdusspring
โ”‚  โ”‚  โ”‚      โ”œโ”€ common
โ”‚  โ”‚  โ”‚      โ”œโ”€ controller
โ”‚  โ”‚  โ”‚      โ”œโ”€ domain
โ”‚  โ”‚  โ”‚      โ”œโ”€ dto
โ”‚  โ”‚  โ”‚      โ”œโ”€ interceptor
โ”‚  โ”‚  โ”‚      โ”œโ”€ respository
โ”‚  โ”‚  โ”‚      โ””โ”€ service
โ”‚  โ”‚  โ””โ”€ test
โ”‚  โ”‚     โ””โ”€ java.ourdus.ourdusspring
โ”‚  โ”‚
โ”‚  โ”œโ”€ build.gradle
โ”‚  โ”œโ”€ gradlew
โ”‚  โ”œโ”€ gradlew.bat
โ”‚  โ””โ”€ settings.gradle
โ”‚
โ”‚
โ””โ”€โ”€ Dockerfile


(document)
.
โ””โ”€ document/crawling
    โ”œโ”€ crawling-code
    โ”‚  
    โ””โ”€ crawling-csv

ํ”„๋กœ์ ํŠธ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•

  1. use dockerfile
docker build -f Dockerfile.dev . -t name

docker run -d -p 8080:8080 name 
  1. console (window)
cd ourdus-spring

gradlew build

cd build/libs

java -jar -Dspring.profiles.active=dev ourdus-spring-0.0.1-SNAPSHOT.jar
  1. Intellij ์„ค์ •
File>Settings>Gradle>Gradle JVM: version11

File>Settings>Java Compiler>version: 11

Run/Debug Configuration>Environment>VM Options: -Dspring.profiles.active=dev

ourdusbe's People

Contributors

hwagyeong avatar ksy991018 avatar woo-yu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

ourdusbe's Issues

Product, Promotion (delete, update) issue

data jpa์‚ฌ์šฉ์‹œ ok (200)์ด ๋‚˜์™€๋„ db์—์„œ ๋ณ€ํ™”๊ฐ€ ์—†์Œ => ์ž„์‹œ ํ•ด๊ฒฐ์ฑ…-> repository ๋‘๊ฐœ ๋™์‹œ ์‚ฌ์šฉํ•ด์„œ update,delete ์ฟผ๋ฆฌ๋กœ ์ž‘์„ฑ

์›์ธ : hibernate์ฟผ๋ฆฌ๋ฅผ ํ™•์ธํ•ด๋ณด๋‹ˆ select ๋งŒ ์‹คํ–‰๋˜๊ณ  delete, update๊ฐ€ ์‹คํ–‰์•ˆ๋จ => ์ด์œ  ๋ถˆ๋ช…,,?

API ํ˜ธ์ถœ์‹œ Parameter๋กœ Collection์„ ๋ณด๋‚ผ๋•Œ ๋ฌธ์ œ

Requestbody๋กœ List๋ฅผ ๋ฐ›์œผ๋ คํ•˜๋ฉด,

@PostMapping("{product_id}/option/new")
    public ApiResult<String> saveOption(@PathVariable("product_id") Long productId, @RequestBody List<String> names, @RequestBody List<ProductChildOptionRequest> childOptionRequests){
        LinkedList<ProductChildOptionRequest> linkedList = new LinkedList<>();
        linkedList.addAll(childOptionRequests);
        productOptonService.saveOption(productId, names, linkedList);
        return OK("์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
    }

์•„๋ž˜์˜ error ๋ฉ”์„ธ์ง€๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

"message": "JSON parse error: Cannot deserialize instance of `java.util.ArrayList<java.lang.Object>` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<java.lang.Object>` out of START_OBJECT token\n at [Source: (PushbackInputStream); line: 1, column: 1]",

๋ฌธ์ œ์— ๋Œ€ํ•œ ํ•ด๋‹ต์€ ์ด์ชฝ ๋งํฌ ์—์„œ ์ฐพ์•˜๋‹ค.
JSON์€ Collection์œผ๋กœ deserialize ๋˜์ง€ ์•Š๊ธฐ๋•Œ๋ฌธ์— ์ผ์–ด๋‚˜๋Š” ๋ฌธ์ œ์ด๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์†Œ๊ฐœ๋˜์—ˆ๋Š”๋ฐ, json์œผ๋กœ ๋„˜์–ด์˜ค๋Š” ๊ฐ’์„ ๋ณ€๋™ํ•˜๊ธฐ๋Š” ๋ถˆํŽธํ•ด์„œ paramter๋ฅผ ๋‹ด๋Š” ์ผ๊ธ‰ ์ปฌ๋ ‰์…˜์„ ์„ ์–ธํ•ด์ค€๋‹ค.
(๊ทธ๋ฆฌ๊ณ  parameter์— @RequestBody ๋„ ๋‘๋ฒˆ์ด๋‚˜ ์„ ์–ธ๋˜์–ด์„œ ํ•ด๋‹น ์—๋Ÿฌ๋„ ๊ฐ™์ด ๋œฌ๋‹ค. I/O error while reading input message; nested exception is java.io.IOException: Stream closed )

๋ณ€๊ฒฝ๋œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

@PostMapping("{product_id}/option/new")
    public ApiResult<String> saveOption(@PathVariable("product_id") Long productId, @RequestBody OptionRequest optionRequest){
        LinkedList<ProductChildOptionRequest> linkedList = new LinkedList<>();
        linkedList.addAll(optionRequest.getChildOptionRequests());
        productOptonService.saveOption(productId, optionRequest.getNames(), linkedList);
        return OK("์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
    }

@Getter
@Setter
class OptionRequest {
    private List<String> names;
    private List<ProductChildOptionRequest> childOptionRequests;
}

jwt ํ† ํฐ ๊ฒฝ๋กœ์„ค์ •

ํ† ํฐ ์ ์šฉ ๊ฒฝ๋กœ์™€ ์ ์šฉ์ œ์™ธ ๊ฒฝ๋กœ๋ฅผ ๊ตฌ๋ณ„ํ•ด์„œ uri๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ• ๊ฒƒ๊ฐ™์Œ.

ํ˜„์žฌ ์„ค์ • :
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(jwtInterceptor).addPathPatterns("/api/") //๊ธฐ๋ณธ ์ ์šฉ ๊ฒฝ๋กœ
.excludePathPatterns(Arrays.asList("/api/user/
")); //์ ์šฉ ์ œ์™ธ ๊ฒฝ๋กœ
}

ex)
๋Œ“๊ธ€ ์ˆ˜์ •,์ถ”๊ฐ€,์‚ญ์ œ ๊ฒฝ์šฐ์—๋Š” ์ ‘์†ํ•œ ์œ ์ €์˜ ํ† ํฐ์„ ๋‹ฌ๊ณ ๋‹ค๋…€์•ผ ํ•˜์ง€๋งŒ, ๋Œ“๊ธ€ ๋ณด๊ธฐ๋Š” ๋น„๋กœ๊ทธ์ธ ์œ ์ €๋„ ๋ณผ์ˆ˜ ์žˆ์–ด์•ผํ•จ.
์ž‘ํ’ˆ๋˜ํ•œ Read๋ฅผ ๋บ€ ๊ฒฝ์šฐ์—๋Š” ํ† ํฐ์ด ํ•„์š”ํ•˜์ง€๋งŒ, Read๋Š” ๋น„๋กœ๊ทธ์ธ ์œ ์ €๋„ ๋ณผ์ˆ˜ ์žˆ์–ด์•ผํ•จ. ์œ ์ €๋ถ€๋ถ„๋„ ๋™์ผ.

ํ˜„์žฌ ๋ช…์„ธ์„œ ์ž‘์„ฑํ•œ๊ฒƒ ์ค‘, ํ† ํฐ์ด ํ•„์š”์—†๋Š” uri ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณด๋ฉด

  1. ์ž‘ํ’ˆ ์ „์ฒด ๋ฆฌ์ŠคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ : /api/w/product?page={ํŽ˜์ด์ง€๋„˜๋ฒ„}&size={๊ฐ€์ ธ์˜ฌ์ž‘ํ’ˆ๊ฐฏ์ˆ˜}
  2. ํŠน์ • ์นดํ…Œ๊ณ ๋ฆฌ ์ž‘ํ’ˆ ๋ฆฌ์ŠคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ : /api/w/category/{category_id}
  3. ํŠน์ • ์ž‘ํ’ˆ ๊ฐ€์ ธ์˜ค๊ธฐ : /api/w/product/{product_id}
  4. ํšŒ์›๊ฐ€์ž… : /api/user/join
  5. ํšŒ์› ๋กœ๊ทธ์ธ : /api/user/login
  6. ํšŒ์› ์ด๋ฉ”์ผ ์ฐพ๊ธฐ : /api/user/id-finding
  7. ํšŒ์› ๋น„๋ฐ€๋ฒˆํ˜ธ ์ฐพ๊ธฐ : /api/user/pw-finding

ํ† ํฐ์ด ํ•„์š”์—†๋Š” uri๋“ค ๊ตฌ๋ถ„์„ ์œ„ํ•ด /api/t/producdt/~~ ์‹์œผ๋กœ t๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ
๋ฐ‘์—์™€ ๊ฐ™์ด ์„ค์ •ํ•˜๋Š”๊ฒƒ์„ ์ƒ๊ฐ์ค‘.

๋ฐ”๊พผ ์„ค์ • :
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(jwtInterceptor).addPathPatterns("/api/") //๊ธฐ๋ณธ ์ ์šฉ ๊ฒฝ๋กœ
.excludePathPatterns(Arrays.asList("/api/t/
")); //์ ์šฉ ์ œ์™ธ ๊ฒฝ๋กœ
}

์ด๋ฐ–์˜ ์˜๊ฒฌ์ œ์‹œ ํ™˜์˜~

Promotion & PromotionProduct ํ…Œ์ด๋ธ” delete ๋ฌธ์ œ

  1. PromotionProduct ๋Š” Promotion์„ ์ฐธ์กฐํ•˜๊ณ  ์žˆ์Œ
  2. PromotionProduct ๋Š” Product ๋„ ์ฐธ์กฐํ•˜๊ณ ์žˆ์Œ
  3. Promotion์„ ์ง€์šธ๋•Œ product_id constraint๊ฐ€ ๋ฐœ์ƒ

์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ฐธ์กฐํ•œ ๋‚ด์šฉ
https://modimodi.tistory.com/22

=> ์ƒ๊ฐ๋Œ€๋กœ ์•ˆ์ง€์›Œ์ง,,

docker image build๋ฅผ ์ตœ์ ํ™”ํ•˜๋Š” ๊ณผ์ •

docker image ์ƒ์„ฑ์‹œ ๊ธฐ์กด์˜ ๋ฐฉ์‹์€

๋กœ์ปฌ์—์„œ jar ์ƒ์„ฑ -> jar๋ฅผ ์ด์šฉํ•ด docker build๋กœ ์ด๋ฏธ์ง€ ์ƒ์„ฑ

์ด๋ฒˆ์—๋Š” jar๋ฅผ ์ฐธ์กฐ๋งŒํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ์ง์ ‘ build ๊ณผ์ •๊นŒ์ง€ ํ•œ๋ฒˆ์— ์ด๋ฃจ์–ด์ง€๊ฒŒ๋” ์š•์‹ฌ๋‚ด์„œ ๊ตฌ์„ฑํ•ด๋ณด์•˜์œผ๋‚˜,

FROM gradle:6.8.3-jdk11 AS builder
WORKDIR /app
COPY --chown=gradle:gradle ./ourdus-spring ./
RUN gradle build --no-daemon


FROM openjdk:11-jre-slim
EXPOSE 8080
ARG JAR_FILES=/app/build/libs/*.jar
COPY --from=builder ${JAR_FILES} Ourdus-springboot.jar
ENTRYPOINT [ "java", "-Dspring.profiles.active=dev", "-jar", "Ourdus-springboot.jar" ]

wsl์—์„œ๋Š” ์ž˜ ์ž‘๋™ํ•˜๋Š”๋ฐ, ๊ฐœ์ธ์šฉ free tier EC2์—์„œ๋Š” buildํ•˜๋‹ค๊ฐ€ ์ž๊พธ ๋ป—์–ด๋ฒ„๋ฆฐ๋‹ค...
๊ด€๋ จํ•ด์„œ ์ฐพ์•„๋ณด๋‹ˆ, gradle์ด ๋นŒ๋“œํ• ๋•Œ๋งˆ๋‹ค ์ข…์†์„ฑ์„ ๋‹ค์‹œ ๋‹ค ๋‹ค์šด๋กœ๋“œํ•ด์„œ ๊ทธ๋Ÿฐ ๊ฒƒ์ด๋ผ ์˜ˆ์ƒ.

๊ณต์‹ ๋ฌธ์„œ๋“ค์„ ์ฐพ์•„๋ณด๋ฉด dockerfile ๋‚ด์—์„œ๋Š” build๋ฅผ ์ง„ํ–‰ํ•˜์ง€ ์•Š๋Š”๋ฐ, ์ œ๋Œ€๋กœ ๋งŒ๋“ค์ž๋ฉด ๊ธฐ์กด์˜ jar๋งŒ ์ฐธ๊ณ ํ•˜๋Š” ์ฝ”๋“œ์—์„œ๋„ ๊ฐœ์„ ํ•  ๋ถ€๋ถ„์ด ๋ณด์ธ๋‹ค.

(์ด์ „ ํŒŒ์ผ)
FROM openjdk:11-jre-slim
WORKDIR /app
EXPOSE 8080
ARG JAR_FILES=/app/build/libs/*.jar
COPY --from=builder ${JAR_FILES} Ourdus-springboot.jar
ENTRYPOINT [ "java", "-Dspring.profiles.active=dev", "-jar", "Ourdus-springboot.jar" ]

์ž ๊ทธ๋ ‡๋‹ค๋ฉด, ์–ด๋””๋ฅผ ๊ฐœ์„ ํ•ด์•ผํ• ๊นŒ? Spring docs์™€ Baeldung๋ฅผ ์ฐธ๊ณ ํ•ด๋ณด์ž๋ฉด

This works fine for most applications, but there are a couple of drawbacks.
First, we are using the fat jar created by Spring Boot. This can impact startup time, especially in a containerized environment. We can save startup time by adding the exploded contents of the jar file instead.
Second, Docker images are built in layers. The nature of Spring Boot fat jars causes all application code and 3rd party libraries to be put into a single layer. This means even when only a single line of code changes, the entire layer has to be rebuilt.
By exploding the jar before building, application code and 3rd party libraries each get their own layer. This allows us to take advantage of Docker's caching mechanism. Now, when one line of code is changed, only that corresponding layer needs to be rebuilt.

fat jar๋ฅผ ๋งŒ๋“ค์–ด ์“ฐ๋ฉด ์ปจํ…Œ์ด๋„ˆ ์‹œ์ž‘์‹œ๊ฐ„๋„ ๋Š๋ ค์ง€๊ณ , ์ฝ”๋“œ ํ•˜๋‚˜ ๋ฐ”๋€”๋•Œ๋งˆ๋‹ค rebuildํ•ด์•ผํ•˜๋Š” ๋‹จ์ ์ด ํฌ๋‹ค.
๊ทธ๋ž˜์„œ caching์„ ์ด์šฉํ•ด ํ•œ ์ค„ ๋ณ€๊ฒฝ์‹œ ํ•ด๋‹น layer๋งŒ ๋‹ค์‹œ ๋นŒ๋“œํ•˜๊ฒŒ๋” ๋งŒ๋“ค์–ด์ค€๋‹ค.

<<<< ์ˆ˜์ •ํ•„์š”
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]


FROM adoptopenjdk:11-jre-hotspot as builder
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM adoptopenjdk:11-jre-hotspot
WORKDIR /app
EXPOSE 8080
COPY --from=builder dependencies/ ./
COPY --from=builder snapshot-dependencies/ ./
COPY --from=builder spring-boot-loader/ ./
COPY --from=builder application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

FROM openjdk:11-jre-slim


ARG JAR_FILES=/app/build/libs/*.jar
COPY ${JAR_FILES} Ourdus-springboot.jar
ENTRYPOINT [ "java", "-Dspring.profiles.active=dev", "-jar", "Ourdus-springboot.jar" ]

11

{
    "name" : "๋ง›์ง‘"
}

[โ—Assignment] ์ „์ฒดํ†ตํ•ฉ(2/7)๊นŒ์ง€ ํ•„์š”ํ•œ ์‚ฌํ•ญ

์ง„ํ–‰ํ•ด์•ผํ•˜๋Š” ๋ชฉ๋ก

  • mainํ™”๋ฉด

    Product์˜ Paging ๅฟ…
    ๊ด€๋ จ ๋งํฌ 1, 2

  • 1.2.1 - ํšŒ์›๊ฐ€์ž…

    Jwt๋ฅผ ํ™œ์šฉํ•œ ๋กœ๊ทธ์ธ ๋ฐ ์ธ์ฆ ์œ ์ง€ ๅฟ…

  • 1.3 - ID/Password ์ฐพ๊ธฐ

    ํ•ธ๋“œํฐ๋ฒˆํ˜ธ๋กœ ID(email), ํ•ธ๋“œํฐ๋ฒˆํ˜ธ+ID(email)๋กœ passwd ์ฐพ๊ธฐ
    Email ์ธ์ฆ ๅฟ… (spring boot starter mail package ์ด์šฉ)
    *์ด์™ธ์— ์•„์ด๋””์–ด ์žˆ์œผ์‹œ๋ฉด ์˜๊ฒฌ ์ฃผ์„ธ์š”!
    ๊ทธ๋ฆฌ๊ณ , ID/PW ๋‘˜๋‹ค ํ•ธ๋“œํฐ ๋ฒˆํ˜ธ ์ธ์ฆ์œผ๋กœ ์ฐพ๋Š”๋ฐ ์ด๋ฉ”์ผ ์ธ์ฆ์œผ๋กœ ์ฐพ๊ฒŒ ๋œ๋‹ค๋ฉด, ID๋Š” ์–ด๋–ป๊ฒŒ?? (์ด๋ฉ”์ผ == ์ž…๋ ฅ ID)

  • 2.3 - ๋‚ด ์ •๋ณด

    GET user/{user_id}์—์„œ ํ•ด๋‹น ํ™”๋ฉด์— ํ•„์š”ํ•œ ์ •๋ณด ์กฐํšŒํ•ด์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” mapping ๅฟ…
    ๋‚ด ์ •๋ณด ๋„ค ๊ฐ€์ง€ ์ค‘์—์„œ๋Š”์ ๋ฆฝ๊ธˆ๋งŒ ๊ตฌํ˜„ ๊ฐ€๋Šฅ, ๋‚˜๋จธ์ง€๋Š” ์•„์ง x๋ผ ํ•˜๋“œ์ฝ”๋”ฉ ์š”์ฒญ

  • 2.3.1 - ์ฃผ๋ฌธ ๋‚ด์—ญ

    2.3์˜ ์—ฐ์žฅ์„ . ์ฃผ๋ฌธ(Order) Entity ์ƒ์„ฑ ๋ฐ ์กฐํšŒ ๊ตฌํ˜„ ๅฟ…

  • 2.3.4 - ํšŒ์›์ •๋ณด ์ˆ˜์ • ๋ฐ ์ฃผ์†Œ ์ถ”๊ฐ€/์ˆ˜์ •

    ์ฃผ์†Œ(๋ฐฐ์†ก์ง€, Address) Entity ์ƒ์„ฑ ๋ฐ CRUD ๊ตฌํ˜„ ๅฟ…

  • 2.3.5 ํšŒ์›ํƒˆํ‡ด
  • ๐Ÿ’ฅ Data Crawling

    ์ž„์‹œ ๋ฐ์ดํ„ฐ๋กœ ์“ธ ๋‚ด์šฉ๋“ค(User, ๋ฐฐ์†ก์ง€, ์ฃผ๋ฌธ ๋“ฑ๋“ฑ ๊ธ์–ด์˜ฌ์ˆ˜ ์—†๋Š”๊ฒƒ), ์ž‘ํ’ˆ ๋ฐ ์ž‘ํ’ˆ ์—ฐ๊ด€ table๋“ค

ํ”„๋ก ํŠธ์—๊ฒŒ ์š”์ฒญํ•˜๋Š” ์‚ฌํ•ญ

  1. 2.3 ๋‚ด ์ •๋ณด์—์„œ ์ ๋ฆฝ๊ธˆ์„ ์ œ์™ธํ•œ ์„ธ ๊ฐ€์ง€ ํ•ญ๋ชฉ ํ•˜๋“œ์ฝ”๋”ฉ ์š”์ฒญ
  2. ID/PW ์ฐพ๊ธฐ์—์„œ ID๋Š” ๋ฌด์—‡์„ ๊ตฌ๋ถ„์ž๋กœ?
  3. Jwt๋ฅผ ํ™œ์šฉํ•œ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•  ์˜ˆ์ •, ์ฐธ๊ณ  ์š”์ฒญ
  4. ์ž‘ํ’ˆ/ํด๋ž˜์Šค์˜ Option๋ถ€๋ถ„ ๊ฐ„์†Œํ™” ์š”์ฒญ

์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์˜์‚ฌํ•ญ

  1. ์ตœ๋Œ€ํ•œ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ์ฒ˜๋ฆฌํ• ๊ฒƒ (์–ด์ฉ”์ˆ˜ ์—†๋Š”๊ฒฝ์šฐ์—๋งŒ ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„ ์‚ฌ์šฉ)

  2. ์ฃผ์ธ์„ ์™ธ๋ž˜ํ‚ค ์žˆ๋Š”๊ณณ์œผ๋กœ ์ง€์ •ํ•  ๊ฒƒ

  3. ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์‹œ ๋ฌดํ•œ๋ฃจํ”„ ์ฃผ์˜ (toString, lombok, JSON ์ƒ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)

    โ†’ toString ์‚ฌ์šฉ์ž์ œ

    โ†’ lombok์—์„œ toString ์ƒ์„ฑ์€ ๋นผ๊ณ  ์‚ฌ์šฉ

    โ†’ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์„๋•Œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋ง๊ณ  DTO๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉ

  4. ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„ ์‚ญ์ œ ์˜ค๋ฅ˜ (์ œ์•ฝ์กฐ๊ฑด ๋•Œ๋ฌธ์—)
    https://stackoverflow.com/questions/21569970/how-to-delete-child-or-parent-objects-from-relationship
    https://velog.io/@sonaky47/JPA-delete%EA%B0%80-%EC%95%88%EB%90%9C%EB%8B%A4
    https://hellokoding.com/deleting-data-with-jpa-hibernate/

์ž‘ํ’ˆ(Product)๊ณผ ์ฃผ๋ฌธ(Order)์„ ์„ค๊ณ„ํ•˜๋Š” ๊ณผ์ •

์ž‘ํ’ˆ(Product)๊ณผ ์ฃผ๋ฌธ(Order)์„ ์„ค๊ณ„ํ•˜๋Š” ๊ณผ์ •

1. ์ž‘ํ’ˆ์„ ์ €์žฅํ•ด๋ณด์ž.

image
ํ•œ ์ž‘ํ’ˆ๋งˆ๋‹ค ๊ณ ์œ ์˜ ์ด๋ฆ„๊ณผ, ๊ณ ์œ ์˜ ์ž‘๊ฐ€, ๋ฐฐ์†ก๋น„, ๋ช‡๋ช… ๊ตฌ๋งคํ–ˆ๋Š”์ง€ ๊ตฌ๋งค์ˆ˜, ... ๋“ฑ๋“ฑ์ด ํฌํ•จ๋˜์–ด์žˆ๋‹ค. ์ด์ค‘์—์„œ ๊ณ ๋ คํ•ด์•ผํ•  ์ ์€ ํ•œ ์ž‘ํ’ˆ์€ ํ•˜๋‚˜์˜ ์นดํ…Œ๊ณ ๋ฆฌ์— ์†ํ•œ๋‹ค๋Š” ๊ฒƒ๊ณผ, ์˜ต์…˜๊ณผ ์ด๋ฏธ์ง€๋Š” ์—ฌ๋Ÿฌ๊ฐœ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
๊ฐœ๊ฐœ๋ณ„์˜ ์ž‘ํ’ˆ์ด ์†ํ•ด์•ผํ•˜๋Š” ๋ถ„๋ฅ˜์ธ PROUDCT_CATEGORY๋ฅผ ์„ ์–ธํ•ด์ฃผ๊ณ , ๊ทธ ์นดํ…Œ๊ณ ๋ฆฌ์˜ PK์ธ category_id๋ฅผ FK๋กœ ์ €์žฅํ•ด๋‘”๋‹ค.

image

๋‹ค์Œ์œผ๋กœ๋Š” ์˜ต์…˜๊ณผ ์ด๋ฏธ์ง€๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž. PRODUCT ํ…Œ์ด๋ธ”์˜ column์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์˜ต์…˜์ด๋‚˜ ์ด๋ฏธ์ง€๋ฅผ ๋„ฃ๊ธฐ์—๋Š” atomic value๋ฅผ ๋ณด์žฅํ•˜์ง€ ๋ชปํ•œ๋‹ค. (์ œ1์ •๊ทœํ™” ํ•„์š”)

์—ฌ๋Ÿฌ ๊ฐœ์˜ column์„ ๋งŒ๋“ค๋ฉด ๋˜์ง€ ์•Š์„๊นŒ? column๋งˆ๋‹ค ์ด๋ฏธ์ง€1, ์ด๋ฏธ์ง€2, ์ด๋ฏธ์ง€3 ...
์ด๋Ÿฐ ๋ฐฉ๋ฒ•์€ ์ด๋ฏธ์ง€์˜ ์ตœ๋Œ€ ๊ฐœ์ˆ˜๊ฐ€ ๊ณ ์ •๋˜์–ด์žˆ๋‹ค๋ฉด ๊ฐ€๋Šฅํ•  ์ˆ˜๋Š” ์žˆ๊ฒ ์ง€๋งŒ ์ด๋ฏธ์ง€๊ฐ€ ์ฑ„์›Œ์ง€๊ธฐ๊นŒ์ง€ null ๊ฐ’์ด์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด ๋งค์šฐ ํฐ ๋‹จ์ ์œผ๋กœ ์ž‘์šฉํ•œ๋‹ค. ๋ง๋ถ™์—ฌ col๋„ ๋„ˆ๋ฌด ๊ธธ์–ด์ง„๋‹ค.

๊ณ ๋กœ PRODUCT_OPTION๊ณผ PRODUCT_IMAGE ๋ผ๋Š” ํ…Œ์ด๋ธ”์„ ๊ฐ๊ฐ ์ƒ์„ฑํ•ด์ค€๋‹ค.

image

์˜ต์…˜์€ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•ด์ฃผ์–ด์•ผํ• ๊นŒ?
์‚ฌ์‹ค Idus์˜ ์˜ต์…˜์€ ์•„๋ž˜์˜ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ๋‘ ๋ถ„๋ฅ˜์˜ ์˜ต์…˜์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ๋‹ค.
image
์ „์ฒด ์˜ต์…˜๊ณผ ๊ฐ ์ „์ฒด ์˜ต์…˜๋ณ„ ์„ธ๋ถ€ ์˜ต์…˜์ด ์กด์žฌํ•œ๋‹ค.
์ด๋Ÿฐ ๊ตฌ์กฐ๋ฅผ ๋„๋ ค๋ฉด ์ „์ฒด ์˜ต์…˜์—๋Š” parent_option, ์„ธ๋ถ€ ์˜ต์…˜์—๋Š” child_option์œผ๋กœ ์ƒ์„ฑํ•ด์ฃผ์–ด ๊ฐ parent๋ณ„ child ์˜ต์…˜์„ ํ•˜๋‚˜์”ฉ ์„ ํƒํ•ด์ฃผ์–ด ์ฃผ๋ฌธ์„ ํ•˜๊ฒŒ๋” ๊ตฌ์„ฑ๋˜์–ด์žˆ๋‹ค.
์˜ต์…˜์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ž‘ํ’ˆ์—๋Š” ์˜ต์…˜ ์„ ํƒ ์—†์ด ์ž‘ํ’ˆ์˜ ์ •๋ณด๋งŒ ๊ตฌ์„ฑ๋œ๋‹ค.

2. ์ฃผ๋ฌธ์„ ํ•ด๋ณด์ž.

์ž‘ํ’ˆ์˜ ๊ฐ ์„ธ๋ถ€์˜ต์…˜์„ ํด๋ฆญํ•ด์ฃผ๋ฉด
image

์ „์ฒด์˜ต์…˜:์„ธ๋ถ€์˜ต์…˜/์ „์ฒด์˜ต์…˜:์„ธ๋ถ€์˜ต์…˜/์ „์ฒด์˜ต์…˜:์„ธ๋ถ€์˜ต์…˜
์ˆ˜๋Ÿ‰(1)                     ๊ฐ€๊ฒฉ product ๊ฐ€๊ฒฉ+์˜ต์…˜ ๊ฐ€๊ฒฉ๋“ค

์˜ ํ˜•ํƒœ์˜ ์ฃผ๋ฌธ์–‘์‹์ด ์ง€์ •๋œ๋‹ค.
๊ฐ ์ฃผ๋ฌธ์–‘์‹์€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํ˜น์€ ์ฃผ๋ฌธํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ํด๋ฆญ์‹œ ์ž‘๊ฐ€์˜ id์™€ ์ด๋ฆ„, ์ž‘ํ’ˆ์˜ id์™€ ์ด๋ฆ„๋„ ํ•จ๊ป˜ ๋„˜๊ฒจ์ง„๋‹ค.

์ฃผ๋ฌธํ•˜๊ธฐ์™€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํด๋ฆญ์‹œ ๋กœ๊ทธ์ธ์ด ๋˜์–ด์žˆ์ง€ ์•Š์œผ๋ฉด (Jwt ํ™•์ธ) ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ redirect ๋œ๋‹ค.

2-1 '๊ตฌ๋งคํ•˜๊ธฐ' ๋ฅผ ํ†ตํ•œ ๋ฐ”๋กœ ์ฃผ๋ฌธ

๊ตฌ๋งคํ•˜๊ธฐ๋ฅผ ๋ˆ„๋ฅผ ๊ฒฝ์šฐ cart_temp_id๋ผ๋Š” uuid๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ํ•ด๋‹น ์ฃผ๋ฌธ์–‘์‹๋“ค์— ์ €์žฅ๋˜์–ด์žˆ๋˜ ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•ด์ค€๋‹ค.
image
image

์ž‘๊ฐ€์˜ id ๋ฐ ์ด๋ฆ„, ์ž‘ํ’ˆ์˜ id ๋ฐ ์ด๋ฆ„, ๊ทธ๋ฆฌ๊ณ  ๋‘ ๊ฐ€์ง€์˜ ์ฃผ๋ฌธ์–‘์‹์˜ ์ •๋ณด๋ฅผ ํ•จ๊ป˜ ์ „๋‹ฌํ•œ๋‹ค.
'๋ฐ”๋กœ์ฃผ๋ฌธ' ํ˜•ํƒœ์—์„œ๋Š” X ๋ฒ„ํŠผ์œผ๋กœ ํ•ญ๋ชฉ์˜ ์‚ญ์ œ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

๋กœ๊ทธ์ธ์ด ๋˜์–ด์žˆ์ง€ ์•Š๋Š”๋‹ค๋ฉด redirect ์‹œํ‚ค๊ณ , ๊ทธ ๋•Œ cart_temp_ip๋Š” query paramter์— ๋‹ด๊ฒจ์„œ ์ „๋‹ฌ๋œ๋‹ค.
image

2-2. '์žฅ๋ฐ”๊ตฌ๋‹ˆ'๋ฅผ ํ†ตํ•œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‹ด๊ธฐ ํ›„ ์ฃผ๋ฌธ

์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด๊ฒŒ๋œ๋‹ค๋ฉด ์ฃผ๋ฌธ์–‘์‹์˜ ์ •๋ณด๋“ค์„ cart_detail์— ๋‹ด๊ฒŒ๋œ๋‹ค.
cart_detail์—๋Š”

image

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ •๋ณด๋“ค์ด ํ•„์š”ํ•  ๊ฒƒ์ด๋‹ค.

๋‹ด์€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์˜ ์ •๋ณด๋“ค์€
image
์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฒ„ํŠผ์„ ํด๋ฆญ์‹œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ detail์˜ ์ „์ฒด list๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.
image
๋ฐ”๋กœ์ฃผ๋ฌธ๊ณผ๋Š” ๋‹ฌ๋ฆฌ X ๋ฒ„ํŠผ ํ˜น์€ ํ†ฑ๋‹ˆ๋ฐ”ํ€ด ๋ฒ„ํŠผ์„ ํ†ตํ•ด ์‚ญ์ œ or ์˜ต์…˜์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

  • ์˜ต์…˜ ์ˆ˜์ • ๊ธฐ๋Šฅ์˜ ๊ตฌํ˜„?

์ดํ›„์— ๊ฒฐ์ œํ•˜๋Š” ๋ฐฉ์‹์€ ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹์ด ๋™์ผํ•˜๋‹ค.

3. ๊ฒฐ์ œํ•ด์„œ ์ฃผ๋ฌธํ•˜๊ธฐ

๊ฒฐ์ œํ•˜๊ธฐ์—์„œ๋Š” ์ฃผ๋ฌธํ•˜๋ ค๋Š” ์‚ฌ๋žŒ์˜ ์ •๋ณด (์ด๋ฆ„, ์ „ํ™”๋ฒˆํ˜ธ)์™€ ์ฃผ์†Œ์ง€ ์„ธ๊ฐ€์ง€๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๊ฒŒ ์„ค์ •ํ•ด๋‘์—ˆ๊ณ 
์ฃผ๋ฌธ ์ž‘ํ’ˆ ์ •๋ณด์—์„œ๋Š” ์ฃผ๋ฌธ์–‘์‹ ํ˜•ํƒœ๋กœ ๋ถˆ๋Ÿฌ์˜จ ์ •๋ณด๋“ค์„ ์ „๋‹ฌ๋ฐ›์•„ ํ•ด๋‹น ์ •๋ณด๋“ค์„ ํ‘œ์‹œํ•ด์ค€๋‹ค.

image

๊ฒฐ์ œ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด
์•„๋ž˜์™€ ๊ฐ™์ด ์ฃผ๋ฌธ์ •๋ณด (ORDER) ๋ผ๋Š” ํ…Œ์ด๋ธ”์— ์ฃผ๋ฌธํ•œ ๋‚ ์งœ์™€ ์ด ๊ฐ€๊ฒฉ ๋ฐ ๊ฒฐ์ œ์ •๋ณด๋ฅผ ์ €์žฅํ•ด์ฃผ๊ณ 
๊ฐœ๊ฐœ๋ณ„์˜ ์ฃผ๋ฌธ์–‘์‹๋“ค์„ ์ฃผ๋ฌธ์ƒ์„ธ (ORDER_DETAIL) ํ…Œ์ด๋ธ”์— ์ €์žฅํ•ด์ค€๋‹ค.
image

image
์ด๋•Œ ์ฃผ๋ฌธ ์ƒ์„ธ๋ฒˆํ˜ธ๋Š” ๊ฐ ์ฃผ๋ฌธ๋ฒˆํ˜ธ๋‹น ํ•ด๋‹นํ•˜๋Š” ์ฃผ๋ฌธ์–‘์‹๋“ค์˜ ์ˆœ์„œ๋ฅผ 1, 2, 3 ์ˆœ์œผ๋กœ ์ง€์ •ํ•˜์—ฌ ๋„ฃ์–ด์ค€๋‹ค.

์Šคํ”„๋ง๋ถ€ํŠธ ๊ฐ•์˜ ํ•„๊ธฐ&๊ณต๋ถ€

#์Šคํ”„๋ง์˜ ํฐ ํŠน์ง• : IoC๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ DI๋ผ๋Š” ๋ฐฉ์‹์„ ํ†ตํ•ด ์˜์กด์„ฑ ์—ญ์ „ ์ œ์–ด๋ฅผ ํ•จ

  1. IoC (Inversion of Control)
  • ์ œ์–ด๊ฐ€ ์—ญ์ „๋จ : ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๊ฐ์ฒด๋ฅผ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๊ณ  ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ง์ ‘ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ํ•ด๋‹น ๊ฐ์ฒด์— ์ฃผ์ž… ์‹œ์ผœ์ค€ ๊ฒƒ. (B๋ผ๋Š” ๊ฐ์ฒด๊ฐ€ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์—์„œ ๊ด€๋ฆฌ๋˜๊ณ  ์žˆ๋Š” Bean ์ด๋ผ๋ฉด @Autowired๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…๋ฐ›๋Š”์ƒํ™ฉ).
  • ๊ฐ์ฒด ์ƒ๋ช… ๊ด€๋ฆฌ, ํ๋ฆ„ ์ œ์–ด๋ฅผ ์ œ 3์ž์—๊ฒŒ ์œ„์ž„ํ•˜๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ
  1. DI(Dependency Injection)
  • ์˜ค๋ธŒ์ ํŠธ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ์™ธ๋ถ€(์Šคํ”„๋ง์ปจํ…Œ์ด๋„ˆ)์—์„œ ์ฃผ์ž…๋ฐ›๊ณ , ์ด๋ฅผ ํ†ตํ•ด ์—ฌํƒ€ ์˜ค๋ธŒ์ ํŠธ์™€ ๋‹ค์ด๋‚ด๋ฏนํ•˜๊ฒŒ ์˜์กด๊ด€๊ณ„๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ
  • IoC ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์ค‘ ํ•˜๋‚˜
  • ํด๋ž˜์Šคํƒ€์ž…์ด ๊ณ ์ •๋˜์–ด์žˆ์ฐŒ ์•Š๊ณ , ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ๋‹ค์ด๋‚˜๋ฏนํ•˜๊ฒŒ ๊ตฌํ˜„ ํด๋ž˜์Šค๋ฅผ ๊ฒฐ์ •ํ•ด์„œ ์ œ๊ณต ๋ฐ›์„์ˆ˜ ์žˆ์–ด์•ผํ•จ.

#Spring initializer

  • ์š”์ฆ˜์—๋Š” Maven๋ณด๋‹ค Gradle -> buildํˆด๋กœ dependencies ๊ด€๋ฆฌ
  • gradle : ๋ฒ„์ „์„ค์ •ํ•˜๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋•ก๊ฒจ์˜ค๋Š”๊ฑฐ
  • snapshot ์•„์ง ๋งŒ๋“ค๊ณ ์žˆ๋Š” ๋ฒ„์ „
  • dependencies : ์–ด๋–ค library ๋•ก๊ฒจ์˜ฌ์ง€
  • Thymeleaf : html์„ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํ…œํ”Œ๋ฆฟ ์—”์ง„

#๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ดํŽด๋ณด๊ธฐ

  • ํ•„์š”ํ•œ ์˜์กด์„ฑ๋“ค์„ ๋‹ค ๋•ก๊ฒจ์˜ด.
  • ex) thymeleaf๊ฐ€ ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋•ก๊ฒจ์˜ด. ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ํ•„์š”ํ•œ๊ฑฐ ๋˜ ๋•ก๊ฒจ์˜ด
  • ์ž๋ฐ” ๋ฉ”์ธ๋ฉ”์†Œ๋“œ๋งŒ ์‹คํ–‰ํ•ด๋„ ์›น์„œ๋ฒ„๊ฐ€ ๋œธ. tomcat์„œ๋ฒ„ ๊น”ํ•„์š”์—†์Œ. ๋‚ด์žฅ๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ : embeded

#์Šคํ”„๋ง๋ถ€ํŠธ ์ฃผ์š” ํŠน์ง•

  1. ์Šคํ”„๋ง๋ถ€ํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    spring-boot starter-web
    spring-boot-starter-tomcat : ํ†ฐ์บฃ(์›น์„œ๋ฒ„)
    spirng-webmvc : ์Šคํ”„๋ง ์›น MVC
    spring-boot-starter-thymeleaf: ํƒ€์ž„๋ฆฌํ”„ ํ…œํ”Œ๋ฆฟ์—”์ง„(View)
    spring-boot-starter(๊ณตํ†ต) : ์Šคํ”„๋ง ๋ถ€ํŠธ + ์Šคํ”„๋ง ์ฝ”์–ด + ๋กœ๊น…
    spring-boot
    spring-core
    spring-boot-starter-logging
    logback,slf4j

  2. ํ…Œ์ŠคํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    spring-boot-starter-test
    junit : ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ
    mockito: ๋ชฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    assertj : ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ข€๋” ํŽธํ•˜๊ฒŒ ์ž‘์„ฑ
    spring-test : ์Šคํ”„๋ง ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ง€์›

#Controller ๋งŒ๋“ค๊ธฐ

  • ์–ด๋…ธํ…Œ์ด์…˜ ํ•„์š”. @controller
    -@GetMapping("hello") ๋Š” ์›น์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ /hello๋ผ๊ณ  ๋“ค์–ด์˜ค๋ฉด ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋Š” ๋œป.
  • return hello๋ฉด resources์˜ templates์˜ ํด๋” ๋ฐ‘์— hello.html์„ ์ฐพ์•„์„œ ๋ Œ๋”๋ง์„ ํ•จ
  • ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ฆฌํ„ด๊ฐ’์œผ๋กœ ๋ฌธ์ž๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด viewResolver๊ฐ€ ํ™”๋ฉด์„ ์ฐพ์•„์„œ ์ฒ˜๋ฆฌํ•จ.
    • ์Šคํ”„๋ง ๋ถ€ํŠธ ํ…œํ”Œ๋ฆฟ์—”์ง„ ๊ธฐ๋ณธ viewName ๋งคํ•‘
    • resources:Templates/ + {ViewName} + .html

#์Šคํ”„๋ง ์›น ๊ฐœ๋ฐœ๊ธฐ์ดˆ

  • ์ •์  ์ปจํ…์ธ  : ํŒŒ์ผ์„ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌํ•ด์ฃผ๋Š”๊ฒƒ
  • MVC์™€ ํ…œํ”Œ๋ฆฟ ์—”์ง„(php,jsp): ์„œ๋ฒ„์—์„œ ๋ญ”๊ฐ€ ๋ณ€๊ฒฝ์„ ํ•ด์„œ HTML์— ๋‚ด๋ ค์ฃผ๋Š”๊ฒƒ
  • API : ๋ฐ์ดํƒ€ ๋‚ด๋ ค์ค„๋•Œ

#์ •์ ์ปจํ…์ธ 
-resources -> static -> [~~~].html ์˜ฌ๋ฆฌ๋ฉด ์„œ๋ฒ„ ์‹คํ–‰ํ›„ localhost:8080/[~~~].htmlํ•˜๋ฉด ๊ฒฐ๊ณผ๋ฌผ ๋ฐ”๋กœ ์ถœ๋ ฅ

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ localhost:8080/[~~~].html ์น˜๋ฉด ๋‚ด์žฅ ํ†ฐ์บฃ์„œ๋ฒ„๊ฐ€ ๋ฐ›์•„์™€์„œ ๋จผ์ € ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ฐพ์•„๋ด„. ๊ทธ๋Ÿฌ๋‚˜, ๊ด€๋ จ๋œ ๋งคํ•‘์ด ๋œ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์—†์œผ๋ฉด ๋‚ด๋ถ€ resources: static์— ์žˆ๋Š”๊ฑธ ์ฐพ์Œ. ์ด๋•Œ, ์žˆ์œผ๋ฉด ๋ฐ˜ํ™˜.

#MVC์™€ ํ…œํ”Œ๋ฆฟ ์—”์ง„
MVC: Model, View, Controller

  • ์š”์ฆ˜์€ ๋ทฐ์™€ ์ปจํŠธ๋กค๋กœ ๋‚˜๋ˆ„๋Š”๊ฒŒ ํŠธ๋ Œ๋“œ
  • ๋ทฐ๋Š” ํ™”๋ฉด๊ณผ ๊ด€๋ จ๋œ ์ผ๋งŒ, ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด๋‚˜ ์„œ๋ฒ„ ๋’ท๋‹จ์— ๊ด€๋ จ๋œ ์ผ๋“ค

-thymleaf์˜ ์žฅ์  : html์„ ์จ๋ณด๊ณ  ๋ฐ”๋กœ ์—ด์–ด๋„ ๊ป๋ฐ๊ธฐ๋ฅผ ๋ณผ์ˆ˜์žˆ์Œ
-

hello! empty

์—์„œ hello! empty๋Š” html๋กœ๋งŒ ํ™•์ธํ• ๋•Œ ๋ณด์ด๊ณ , ์‹ค์ œ ์„œ๋ฒ„๋กœ ํƒ€์„œ ์“ฐ๋ฉด ์˜ค๋ฅธ์ชฝ ๊ฐ’์ด ์™ผ์ชฝ ๋‚ด์šฉ๋ฌผ๋กœ ์น˜ํ™˜๋จ.
-required ์˜ต์…˜ : ctrl + p

  • ๊ณผ์ • : ์›น๋ธŒ๋ผ์šฐ์ €์—์„œ localhost:8080/hello-mvc ๋„˜๊ธฐ๋ฉด ์Šคํ”„๋ง๋ถ€ํŠธ ๋„์šธ๋•Œ ๋‚ด์žฅ ํ†ฐ์ผ“์„œ๋ฒ„์— ๋˜์ ธ์ง. ๊ทธ๋Ÿฌ๋ฉด, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์žˆ๋„ค ํ™•์ธํ•˜๊ณ  return๊ฐ’๊ณผ model๋ฅผ viewResolver์— ๋˜์ ธ์คŒ. viewResolver๊ฐ€ ํ…œํ”Œ๋ฆฟ์— ๋˜‘๊ฐ™์€ html์ฐพ์•„์„œ ๋ Œ๋”๋ง ํ•ด์„œ ๋ณ€ํ™˜ํ•œ html์„ ์›น ๋ธŒ๋ผ์šฐ์ €์— ๋„์›€.

#API

  • ๊ฐ์ฒด๋ฐ˜ํ™˜
  • @responsebody ๋Š” ๊ทธ๋Œ€๋กœ data๋ฅผ ์˜ฌ๋ฆผ.
  • ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉด jsonํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜
  • ๊ณผ์ • : ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ localhost:8080/hello-api ์น˜๋ฉด ํ†ฐ์บฃ ๋‚ด์žฅ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์„œ ์Šคํ”„๋ง์ปจํ…Œ์ด๋„ˆ์— ๋˜์ง. ์ปจํŠธ๋กค๋Ÿฌ์— ์ฐพ์•˜๋Š”๋ฐ @responsebody๊ฐ€ ๋ถ™์–ด์žˆ์œผ๋ฉด, http์‘๋‹ต์— ๋ฐ”๋กœ ๋˜์ง. ์ด๋•Œ string์ด๋ฉด stringConverter , ๊ฐ์ฒด๋ฉด jsonConverter ์ด์šฉํ•ด์„œ ์›น์„œ๋ฒ„์— ๋„˜๊น€. (๊ธฐ์กด์—๋Š” viewResolver)
  • ๊ฐ์ฒด -> json ๋ฐ”๊ฟ”์ฃผ๋Š” ๋Œ€ํ‘œ์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Jackson๊ณผ ๊ตฌ๊ธ€์˜ ์ง€์Šจ?. ์Šคํ”„๋ง์—์„  Jackson์‚ฌ์šฉ

#ํšŒ์› ๋„๋ฉ”์ธ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋งŒ๋“ค๊ธฐ

  • ๋„์ด ๋ฐ˜ํ™˜๋ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ์—” optional์„ ๋ถ™์ž„.

#ํšŒ์› ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑ

  • ์ž๋ฐ”์—์„œ๋Š” JUnit์ด๋ผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ์‚ฌ์šฉํ•ด์„œ ํ…Œ์ŠคํŠธํ•จ
  • Assertions => junit ์“ฐ๋Š” ๋ฐฉ๋ฒ• / assertj ์“ฐ๋Š”๋ฐฉ๋ฒ• ๋‘๊ฐ€์ง€
  • alt+enter ๋กœ assertions ์œ„๋กœ ์˜ฌ๋ฆฌ๊ณ  assertThat์œผ๋กœ ๋ฐ”๋กœ์‚ฌ์šฉ
  • ๋น„์Šทํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์‹œ ๋ณต์‚ฌํ•˜๊ณ  ์ด๋ฆ„ ๋ณ€๊ฒฝํ• ๋•Œ ์“ฐ๋Š” ํ‚ค : shift + f6
  • test ๋ฉ”์†Œ๋“œ๋“ค์€ ์ˆœ์„œ ์ •ํ•ด์ ธ์žˆ์ง€์•Š๊ณ  ๊ฐ๊ฐ ์‹คํ–‰๋˜๋ฏ€๋กœ, test ๋๋‚˜๊ณ ๋‚˜๋ฉด data๋ฅผ ํด๋ฆฌ์–ด ํ•ด์ฃผ์–ด์•ผํ•จ
  • @AfterEach ๋ฉ”์†Œ๋“œ ๋๋‚ ๋•Œ๋งˆ๋‹ค ์‹คํ–‰ํ• ๊ฒƒ

#ํšŒ์› ์„œ๋น„์Šค ๊ฐœ๋ฐœ

  • service ํŒจํ‚ค์ง€๋Š” ๋น„์ฆˆ๋‹ˆ์Šค์— ์˜์กด์ ์œผ๋กœ ์„ค๊ณ„, repository๋Š” ๊ธฐ๊ณ„์ ์œผ๋กœ ๊ฐœ๋ฐœ์Šค๋Ÿฝ๊ฒŒ naming
  • alt + ctrl + v : ๋‹จ์ถ•ํ‚ค๋Š” ๋ฐ›์•„์˜ค๋Š” ๋ณ€์ˆ˜ ์ž๋™์ƒ์„ฑ
  • ifPresent : null์ด ์•„๋‹๋•Œ (Optional์ด๋ผ ๊ฐ€๋Šฅ)
  • orElseGet : ๊ฐ’์žˆ์œผ๋ฉด ๊บผ๋ƒ„
  • ์ž์ฃผ์“ฐ๋Š” ๋ฉ”์†Œ๋“œ ๋ฝ‘์•„๋‚ด๊ธฐ : alt+ctrl+shift+T -> extract method

#ํšŒ์› ์„œ๋น„์Šค ํ…Œ์ŠคํŠธ

  • ํด๋ž˜์Šค ์ด๋ฆ„ ๊ฐ–๋‹ค๋Œ€๊ณ  ctrl+Shift+T ๋กœ ๊ฐ„ํŽธํ•˜๊ฒŒ test๋งŒ๋“ค๊ธฐ
  • test ๋ฉ”์†Œ๋“œ๋Š” ๊ณผ๊ฐํ•˜๊ฒŒ ํ•œ๊ธ€๋กœ ๋ฐ”๊ฟ”๋„ ๊ดœ์ฐฎ
  • given / when / then ํŒจํ„ด์œผ๋กœ ์งœ๋ณด๊ธฐ
  • ์˜ค๋ฅธ์ชฝ ๋กœ์ง์ด ์‹คํ–‰๋ ๋•Œ ์™ผ์ชฝ ์˜ˆ์™ธ๊ฐ€ ํ„ฐ์ ธ์•ผํ•จ
    assertThrows(IllegalStateException.class, ()->memberService.join(member2));
  • @beforeeach ๋™์ž‘ํ•˜๊ธฐ์ „์— ๋„ฃ๋Š”๊ฒƒ

#์ปจํŠธ๋กค๋Ÿฌ
#dependency ์ธ์ ์…˜ (์˜์กด๊ด€๊ณ„ ์ฃผ์ž…ํ•ด์ฃผ๋Š”๊ฑฐ)

  • @controller๋ถ™์ด๋ฉด ์Šคํ”„๋ง ๋œฐ๋•Œ ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ƒ์„ฑ์„ํ•ด์„œ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ด€๋ฆฌ๋ฅผ ํ•จ.
  • ์ƒ์„ฑ์ž์— @Autowired๋ถ™์œผ๋ฉด ์Šคํ”„๋ง์ปจํ…Œ์ด๋„ˆ์— ์žˆ๋Š”๊ฑฐ๋ž‘ ์—ฐ๊ฒฐ์‹œ์ผœ์คŒ.
  • ํด๋ž˜์Šค ์œ„์— @service ๋ถ™์œผ๋ฉด ์Šคํ”„๋ง์ด ์˜ฌ๋ผ์˜ฌ๋•Œ ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ์Šคํ”„๋ง์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋กํ•ด์คŒ.
  • ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์™ธ๋ถ€์š”์ฒญ ๋ฐ›๊ณ  ์„œ๋น„์Šค์—์„œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋งŒ๋“ค๊ณ  ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ ๋ฐ์ดํƒ€๋ฅผ ์ €์žฅํ•˜๋Š”๊ฒŒ ์ •ํ˜•ํ™”๋œ ํŒจํ„ด

#์Šคํ”„๋ง ๋นˆ ๋“ฑ๋กํ•˜๋Š” 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•

  • ์Šคํ”„๋ง์€ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ์Šคํ”„๋ง ๋นˆ์„ ๋“ฑ๋กํ•  ๋•Œ, ๊ธฐ๋ณธ์œผ๋กœ ์‹ฑ๊ธ€ํ†ค(์œ ์ผํ•˜๊ฒŒ ํ•˜๋‚˜๋งŒ)์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค. ๊ฐ™์€ ์Šคํ”„๋ง ๋นˆ์ด๋ฉด ๋ชจ๋‘ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค์ž„.
  1. ์ปดํฌ๋„ŒํŠธ ์Šค์บ”๊ณผ ์ž๋™ ์˜์กด๊ด€๊ณ„ ์„ค์ •
  • ์œ„์—์„œ ํ•œ๊ฑฐ. ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ
  • @component ์—๋…ธํ…Œ์ด์…˜ ์žˆ์œผ๋ฉด ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ์ž๋™ ๋“ฑ๋ก
    -@controller, @service, @repository์•ˆ์— ๋‹ค @component ํฌํ•จํ•˜๊ณ ์žˆ์Œ
  1. ์ž๋ฐ”์ฝ”๋“œ๋กœ ์ง์ ‘ ์Šคํ”„๋ง ๋นˆ ๋“ฑ๋กํ•˜๊ธฐ

#๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™(OCP , Open-Closed Principle)
: ํ™•์žฅ์—๋Š” ์—ด๋ ค์žˆ๊ณ , ์ˆ˜์ •, ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€์žˆ๋‹ค
: ๊ธฐ๋Šฅ์„ ์™„์ „ํžˆ ๋ณ€๊ฒฝํ•ด๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด ์ˆ˜์ •ํ• ํ•„์š”๊ฐ€ ์—†์Œ
: ์Šคํ”„๋ง์˜ DI(Dependencies injection) ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ์กด์ฝ”๋“œ๋ฅผ ์ „ํ˜€ ์†๋Œ€์ง€ ์•Š๊ณ , ์„ค์ •๋งŒ์œผ๋กœ ๊ตฌํ˜„ํด๋ž˜์Šค๋ฅผ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ

#ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ
@SpringBootTest : ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์™€ ํ…Œ์ŠคํŠธ๋ฅผ ํ•จ๊ป˜ ์‹คํ–‰ํ•œ๋‹ค
@transactional : ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์— ์ด ์• ๋…ธํ…Œ์ด์…˜์ด ์žˆ์œผ๋ฉด, ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ์ „์— ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜๊ณ , ํ…Œ์ŠคํŠธ ์™„๋ฃŒ ํ›„์— ํ•ญ์ƒ ๋กค๋ฐฑํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด DB์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ค์Œ ํ…Œ์ŠคํŠธ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.

#DB์ ‘๊ทผ๋ฐฉ์‹
1.์ˆœ์ˆ˜ Jdbc

  • ์Šคํ”„๋ง์„ ํ†ตํ•ด datasource๋ฅผ ์ฃผ์ž… ๋ฐ›์Œ
    -Sql๋ฌธ ์ง์ ‘ ๋‹ค์”€
  • connection ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌ. ์ˆ˜๋™์œผ๋กœ ์—ด๊ณ  ๋‹ซ์Œ
  • preparedstatement, resultset๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์–ด๋–ค ์กฐ๊ฑด์„ ์ „๋‹ฌํ•˜๊ณ  ๊ฒฐ๊ณผ๊ฐ’์„ ์ „๋‹ฌ๋ฐ›์Œ.

2.์Šคํ”„๋ง JdbcTemplate

  • JDBC API์—์„œ ๋ณธ ๋ฐ˜๋ณต์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ. sql์€ ์ง์ ‘ ์ž‘์„ฑํ•ด์•ผํ•จ.
  • ์ˆœ์ˆ˜ Jdbc์™€ ๋™์ผํ•œ ํ™˜๊ฒฝ์„ค์ • ํ•˜๋ฉด๋จ

3.JPA

  • ๊ธฐ์กด์˜ ๋ฐ˜๋ณต์ฝ”๋“œ๋Š” ๋ฌผ๋ก ์ด๊ณ , ๊ธฐ๋ณธ์ ์ธ sql๋„ jpa๊ฐ€ ์ง์ ‘ ๋งŒ๋“ค์–ด์„œ ์‹คํ–‰ํ•ด์คŒ.
  • ์ธํ„ฐํŽ˜์ด์Šค ์ œ๊ณต.
  • ๊ตฌํ˜„์€ ์—ฌ๋Ÿฌ์—…์ฒด๋“ค์ด ํ•จ.
  • db๊ฐ€ ์•Œ์•„์„œ ์ƒ์„ฑํ•ด์ฃผ๋Š” ๊ฒƒ์„ GENERATE TYPE.IDENTITY๋ผ๊ณ  ํ•จ
  • ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€ํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ž‘ ๋งคํ•‘ํ•จ
  • EntityManager๋ฅผ ์ฃผ์ž…๋ฐ›์•„์•ผํ•จ
  • jpql : ํ…Œ์ด๋ธ” ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ ๋‚ ๋ฆฌ๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ, ๊ฐ์ฒด ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ๋‚ ๋ฆผ.๊ทธ๋Ÿผ sql๋กœ ๋ฒˆ์—ญ์ด ๋จ. pk๊ธฐ๋ฐ˜์ด ์•„๋‹Œ๊ฒƒ๋“ค.
  • ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ณ€๊ฒฝ์ด @transactional ์•ˆ์—์„œ ์‹คํ–‰๋˜์•ผํ•จ
  • ๊ธฐ๋ณธ์ ์ธ crud ์ง์ ‘ ์ฟผ๋ฆฌ์งคํ•„์š”์—†๊ณ , selectํ• ๋•Œ๋Š” jpql๋Š” ์งœ์•ผํ•จ.
  • sql ๊ตฌ์กฐ๋ฅผ java application ๋‚ด์— ์ ์šฉํ•˜์ง€ ์•Š์•„๋„๋จ. ์กฐ์ธ์ฟผ๋ฆฌ๋ฅผ jpa์—์„œ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์คŒ

4.์Šคํ”„๋ง๋ฐ์ดํ„ฐ JPA

  • JPA๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋„๋ก ๋„์™€์ฃผ๋Š” ๊ธฐ์ˆ 
  • ์Šคํ”„๋ง ๋ถ€ํŠธ์™€ JPA๋งŒ ์‚ฌ์šฉํ•ด๋„ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์ด ๋งŽ์ด ์ฆ๊ฐ€
  • ๊ตฌํ˜„ํด๋ž˜์Šค ์ž‘์„ฑ ์—†์ด ์ธํ„ฐํŽ˜์ด์Šค๋กœ๋งŒ ๊ฐœ๋ฐœ์ด ๋๋‚จ

#๊ฐœ๋…์ •๋ฆฌ

  • Service : ์‹ค์ œ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹œํ–‰. repository๋ฅผ ์ด์šฉํ•˜์—ฌ ์‹ค์ œ๋กœ ์ €์žฅํ•ด์ค˜์•ผํ•จ.
  • Dto ์‚ฌ์šฉํ•ด ์ปจํŠธ๋กค๋Ÿฌ์™€ ์„œ๋น„์Šค ์‚ฌ์ด์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์Œ.
  • Entity : dbํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋˜๋Š” ๊ฐ์ฒด
  • repository: ๋ฐ์ดํ„ฐ ์กฐ์ž‘์„ ๋‹ด๋‹น. ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ƒ์„ฑํ•˜๋ฉฐ JpaRepository๋ฅผ extendsํ•จ. ๊ฐ’์€ ๋งคํ•‘ํ•  entity์™€ id์˜ ํƒ€์ž….
  • ์Šคํ”„๋ง๋นˆ: ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ์˜ํ•ด ๋งŒ๋“ค์–ด์ง€๋Š” ์ž๋ฐ” ๊ฐ์ฒด
  • @Autowired : ์ž๋™์œผ๋กœ ์Šคํ”„๋ง ๋นˆ ๊ฐ์ฒด๋ฅผ ํŠน์ • ์ฐธ์กฐ๋ณ€์ˆ˜์— ๋งคํ•‘ํ•ด์ฃผ๋Š” ๊ฒƒ
  • ์Šคํ”„๋ง ๋นˆ ์–ด๋…ธํ…Œ์ด์…˜ ์ข…๋ฅ˜ : @component(์ƒ์œ„), @service, @controller, @repository, @bean, @configuration
    ์ด๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•œ ๋นˆ๋“ค์„ ๋“ฑ๋กํ•˜๊ณ  ํ•„์š”ํ•œ ๊ณณ์—์„œ @Autowired๋ฅผ ํ†ตํ•ด ์ฃผ์ž…๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ์ด ์ผ๋ฐ˜์ .

๋‚จ์€ ๊ตฌํ˜„ํŒŒํŠธ

  • ์˜จ๋ผ์ธ ํด๋ž˜์Šค ์ฃผ๋ฌธ (ํ˜œ์ง„)

  • ๋‚ด์šฉ์„

  • ์ ์–ด์ค์‹œ๋‹ค

  • ๋‚˜์•„์ค‘์— ๊ตฌํ˜„ํ•  ํŒŒํŠธ
    • ์‹œํ๋ฆฌํ‹ฐ (ํ˜œ์ง„)

[โ—Notice] ๊ฐœ๋ฐœํ™˜๊ฒฝ ํ†ตํ•ฉ์„ ์œ„ํ•œ ๊ณ ๋ ค์‚ฌํ•ญ

  • ํ™˜๊ฒฝ ์…‹ํŒ…์€
    Spring Boot 2.3.8 (JPA)
    Java 11
  • AWS RDS - MySql

์ด๊ฑด ์กฐ๊ธˆ ๊ณ ๋ ค์‚ฌํ•ญ์ธ๊ฒŒ, ๋ฐ”๋กœ ์—ฐ๋™์œผ๋กœ ๋“ค์–ด๊ฐ€๋„ ๊ดœ์ฐฎ์„์ง€? 1. ๋ฐ”๋กœ RDS 2. H2 ์“ฐ๋‹ค๊ฐ€ ํ† ์š”์ผ ๋ฐค์— RDS๋กœ ์—ฐ๋™

ํ•˜๋‹จ์˜ ๊ณ ๋ ค์‚ฌํ•ญ๋“ค์€ ๋‚ด์šฉ์ด ๊ธธ์–ด์„œ ๊ฐ„๋‹จํžˆ ์š”์•ฝํ•ด๋ณด์ž๋ฉด,

  1. Controller์—์„œ ๋ฐ›๋Š” paramter๋ž‘ return type ํ†ต์ผ ๊ถŒ์žฅ
  2. Setter๋ฅผ ์ง€์–‘ํ•ฉ์‹œ๋‹ค -> builder ์‚ฌ์šฉ ๊ถŒ์žฅ

1. Handler Method argument ๋ฐ Return value ํ†ต์ผ

*์ฐธ๊ณ  ํŽ˜์ด์ง€: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-methods ,  https://www.xspdf.com/resolution/58586250.html*
  • Spring Web MVC์—์„œ๋Š” @RequestMapping annotation์„ ํ™œ์šฉํ•ด request๋ฅผ controller method์— mappingํ•  ์ˆ˜ ์žˆ๋‹ค.

Handler Method argument

  • mapping๋ฐ›๋Š” handler method์ธ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋กœ @PathVariable, @RequestParam, @requestbody, @ModelAttribute ๋“ฑ์ด ์กด์žฌ

  • @PathVariable : URI ํ…œํ”Œ๋ฆฟ ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ ex) product/{product_id}

  • @RequestBody
    : HTTP request body์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ. (์ฃผ๋กœ JSON)

    Body content is converted to the declared method argument type by using HttpMessageConverter implementations. See @RequestBody

  • @ModelAttribute
    : ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ๊ณผ validation์ด ์ ์šฉ๋œ model์— ์กด์žฌํ•˜๋Š” ์†์„ฑ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ (์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ์ธ์Šคํ„ด์Šคํ™”๋จ) model์€ HTTP Servlet request parameter์˜ ์ด๋ฆ„๊ณผ ๋งค์น˜๋˜๋Š” ํ•„๋“œ ์ด๋ฆ„์œผ๋กœ ๋ง์”Œ์›Œ์ง„๋‹ค. ์ฐธ๊ณ ๋กœ @RequestParam์€ servlet reuqest paramter๋ฅผ 1:1 ๋งค์นญ, object๋Š” model ์‚ฌ์šฉ

    • ์ดํ›„์— model.setAttribute()๋กœ ์ถ”๊ฐ€ํ•œ ์†์„ฑ๋“ค์€ HttpRequest Servlet์— ์ถ”๊ฐ€๋จ

    For access to an existing attribute in the model (instantiated if not present) with data binding and validation applied. See @ModelAttribute as well as Model and DataBinder.

  • โ—๊ฒฐ๋ก ์ ์œผ๋กœ ์ •๋ฆฌํ•ด๋ณด์ž๋ฉด, http query paramter๋ฅผ ์ด์šฉํ•˜๋Š” pathvariable, modelattribute, requestparam๊ณผ๋Š” ๋‹ฌ๋ฆฌ requestbody๋Š” http request body๋ฅผ ์ด์šฉํ•œ๋‹ค.

    ๋น„๊ต๊ธ€1๊ณผ ๋น„๊ต๊ธ€2 ์„ ๊ผญ ์ฝ์–ด๋ณผ๊ฒƒ!!

Return value
-@ResponseBody (@RestController ์‚ฌ์šฉ์‹œ ์ž๋™ ์ ์šฉ)

1-1. Controller์˜ Return type?

  • ResponseEntity<B>
    : The return value that specifies the full response (including HTTP headers and body) is to be converted through HttpMessageConverter implementations and written to the response. See ResponseEntity.

    ๊ด€๋ จ ๋ฉ”์†Œ๋“œ ์ •๋ฆฌ๋Š” ์ด๊ณณ ์ฐธ๊ณ !

  • ์˜ˆ์‹œ ์ฝ”๋“œ

      @PostMapping("join")
     public ResponseEntity<UserDto> join(@RequestBody JoinRequest joinRequest){
     	UserVo userVo = userService.join(
     	joinRequest.getUserEmail(), joinRequest.getUserPw(), joinRequest.getUserName(), joinRequest.getUserTel());
     	return new ResponseEntity<UserDto>(new UserDto(userVo), HttpStatus.OK);
     }
    

1-2. Exception Handling

์ฐธ๊ณ  : https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler , https://www.baeldung.com/exception-handling-for-rest-with-spring (solution3)
  • @ControllerAdvice๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ @ExceptionHandler methods์™€ ํ•จ๊ป˜ ์ด์šฉ

  • Spring Guide ์˜ˆ์‹œ์ฝ”๋“œ

      @ControllerAdvice
      public  class RestResponseEntityExceptionHandler 
        extends ResponseEntityExceptionHandler { 
      @ExceptionHandler(value = { IllegalArgumentException.class, IllegalStateException.class })  
        protected ResponseEntity<Object> handleConflict( RuntimeException ex, WebRequest request) { 
          String bodyOfResponse = "This should be application specific"; 
          return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request); 
          } 
     }
    
  • ์œ„์˜ ์‚ฌ๋ก€๋“ค์„ ์ข…ํ•ฉํ•ด๋ณธ ์˜ˆ์‹œ ์ฝ”๋“œ (์Šคํ„ฐ๋”” ์ฐธ๊ณ )
    : ResponseEntity<> type์„ ํ™œ์šฉํ•œ ApiResult์™€ ApiError, @ControllerAdvice๋ฅผ ํ™œ์šฉํ•œ AllExcpeitonHandler

    public class ApiResult<T> {
    	private final boolean success;
    	private final T response;
    	private final ApiError apiError;
    	private ApiResult(boolean success, T response, ApiError apiError) {
    	this.success = success;
    	this.response = response;
    	this.apiError = apiError;
    	}
    
    	public static <T> ApiResult<T> OK(T response){ //parameter์™€ return์ด ๋™์ผํ•ด์„œ <T> ApiResult<T>
    		return new ApiResult<>(true, response, null);
    	}
    
    	public static ApiResult<?> ERROR(String errorMessage, HttpStatus status){ //Gerneric<?> ์‚ฌ์šฉ
    		return new ApiResult<>(false, null, new ApiError(errorMessage, status));
    	}
    
    	public boolean isSuccess() {
    		return success;
    	}
    
    	public T getResponse() {
    		return response;
    	}
    
    	public ApiError getApiError() {
    		return apiError;
    	}
    
    	@Override
    	public String toString() {
    		return "ApiResult{" +
    		"success=" + success +
    		", response=" + response +
    		", apiError=" + apiError +
    		'}';
    	}
    }
    
    public class ApiError {
       private final String message;
       private final int status;
       ApiError(String message, HttpStatus status) {
           this.message = message;
           this.status = status.value();
       }
       public String getMessage() {
       		return message;
       }
       public int getStatus() {
       		return status;
       }
       @Override
       public String toString() {
       		return "ApiError{" +
       		"message='" + message + '\'' +
       		", status=" + status +
       		'}';
       }
    }
    
    @ControllerAdvice
    public class AllExceptionHandler {
    
    private ResponseEntity<ApiResult<?>> newResponse(Throwable throwable, HttpStatus status){
       HttpHeaders headers = new HttpHeaders();
       headers.add("Content-Type", "application/json");
       return new ResponseEntity<>(ERROR(throwable.getMessage(), status), headers, status);
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> exceptionAll(Exception e){
       return newResponse(e, HttpStatus.BAD_REQUEST);
       }
    }
    


2. Setter์˜ ์‚ฌ์šฉ์„ ์ตœ์†Œํ™”, ๊ทธ๋ฆฌ๊ณ  Builder Pattern ์ œ์•ˆ

  • Setter๋ฅผ ์ง€์–‘ํ•ด์•ผํ•˜๋Š” ์ด์œ ?

    ์•„์ฃผ ์ž˜ ์ •๋ฆฌ๋œ ์งˆ๋ฌธ๊ณผ ๋น›์˜ํ•œ๋‹˜์˜ ๋‹ต๋ณ€์ด ๋‹ด๊ธด ์ด ๊ธ€์„ ๊ผญ ์ฝ์–ด์ฃผ์„ธ์š”!

  • ์™œ Builder๋ฅผ ์จ์•ผํ•˜๋Š”๊ฐ€?

    Effective Java item2์™€ ๊ด€๋ จ๋œ ๊ธ€ (์•„์ฃผ์ข‹์€์˜ˆ์‹œ ์˜ˆ์‹œ1 ์˜ˆ์‹œ2 ์˜ˆ์‹œ3 ๊ณต์‹๋ฌธ์„œ)์„ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค!
    ์˜ˆ์‹œ3์€ Entity์—์„œ lombok์„ ํ™œ์šฉํ•œ @builder๋ฅผ ์ž˜ ์„ค๋ช…ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ ๊ผญ ์ฝ์–ด๋ณด์„ธ์š”~!

  • ์ ์šฉํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ•˜๋Š” ๊ฑฐ์ง€?
    ํ•ด๋‹น ์ฃผ์†Œ์˜ Product Entity๋ฅผ ๋ณด์ž๋ฉด, ํ˜„์žฌ Setter๋„ ์‚ฌ์šฉํ•˜๊ณ  ๊ทธ์ € ๊ทธ๋Ÿฐ ์ƒ์„ฑ์ž๋กœ Product๋ฅผ ๋งŒ๋“ ๋‹ค.
     @Entity
     @Getter @Setter //TODO Setter ์ˆ˜์ •
     public class Product {
     	@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
     	@Column(name = "product_id")
     	private Long productId;
     	@ManyToOne
     	@JoinColumn(name="author_id")
     	private User user;
     	@ManyToOne
     	@JoinColumn(name= "category_id")
     	private ProductCategory productCategory;
     	@Column(name = "product_name")
     	private String productName;
     	@Column(name= "product_price")
     	@ColumnDefault("0")
     	private int productPrice;
     	@Column(name= "product_rate")
     	@ColumnDefault("0")
     	private int productRate;
     	@Column(name= "product_review_num")
     	@ColumnDefault("0")
     	private int productReviewNum;
     	@Column(name = "product_hit")
     	@ColumnDefault("0")
     	private int productHit;
     	@Column(name = "product_purchase")
     	@ColumnDefault("0")
     	private int productPurchase;
     	@Column(name = "product_option_num")
     	@ColumnDefault("0")
     	private int productOptionNum;
     	
     	public Product(User user, ProductCategory productCategory) {
     		this.user = user;
     		this.productCategory = productCategory;
     	}
     		public Product() {
     	}
     }
    
    -> ์ด ์นœ๊ตฌ๋ฅผ Builder Pattern์œผ๋กœ ๋งŒ๋“ค๋ ค๋ฉด?
    1. Effective Java style

      • ๋ˆ„๊ฐ€ ํ•„์ˆ˜ ์ธ์ž์ธ๊ฑฐ์ง€?

        product๋ฅผ ๊ตฌ์„ฑํ•˜๋Š”๋ฐ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•œ ์š”์†Œ๋“ค์„ ๋– ์˜ฌ๋ ค๋ณด์ž. product๊ฐ€ ์ƒ์„ฑ๋ ๋•Œ form์—์„œ ๋„˜์–ด์™€์•ผ๋งŒํ•˜๋Š” ๊ฐ’๋“ค์ด ๋ญ์˜€๋”๋ผ? authorid์™€ categoryid, ๊ทธ๋ฆฌ๊ณ  ์ด๋ฆ„(productName)๊ณผ ๊ฐ€๊ฒฉ(productPrice)๋„ ํ•„์š”ํ•˜๋‹ค.

        • ๊ทธ ์™ธ์—” ์„ ํƒ์ธ์ž๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.
         (์œ„๋Š” ์ƒ๋žต)
             public static class Builder {
         		private final User user; //User๊ฐ’๋งŒ ๋ณ€๋™๋˜์ง€ ์•Š๋Š”๋‹ค. (update ๋ถˆ๊ฐ€)
         		private ProductCategory productCategory;
         		private String productName;
         		private int productPrice;
         		private int productRate;
         		private int productReviewNum;
         		private int productHit;
         		private int productPurchase;
         		private int productOptionNum;
                 public Builder(User user, ProductCategory productCategory, String productName, int productPrice, int productOptionNum ) {
                     this.user= user;
                     this.productCategory = productCategory;
                     this.productName = productName;
                     this.productPrice = productPrice;
                     this.productOptionNum  = productOptionNum;
                 }
                 public Builder productCategory(ProductCategory  productCategory) {
                     this.productCategory= productCategory;
                     return this;
                 }
                 public Builder productName(String productName) {
                     this.productName= productName;
                     return this;
                 }
                 ...
                 public Product  build(){
                 return new Product(this);
              }
               
             public Product build(Builder builder) {
                 this.user= user;
                 this.productCategory = productCategory;
                 this.productName = productName;
                 this.productPrice = productPrice;
                 ....
                 this.productOptionNum = productOptionNum;
             }
             }
              ```
        
           ์ดํ›„์˜ ์ƒ์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง„ํ–‰ํ•œ๋‹ค.
           Product test = new Product
          				        .Builder(user, productCategory, "์ด๋ฆ„", 30000, 3)
          				        .productHit(3) 	//์ด๊ฑด ๋„ฃ์–ด๋„ ๋˜๊ณ  ์•ˆ๋„ฃ์–ด๋„ ๋ฌด๋ฐฉ, setter์—ญํ• 
          				        .build();
        
      1. Lombok ํ™œ์šฉ

        @Builder(builderClassName="defaultBuilder", builderMethodName="defaultBuilder")
        public Product(User user, ProductCategory productCategory, String productName, int productPrice, int productOptionNum){
           	this.user= user;
            this.productCategory = productCategory;
            this.productName = productName;
            this.productPrice = productPrice;
            this.productOptionNum  = productOptionNum;
           }
         @Builder(builderClassName="ByUserBuilder", builderMethodName="ByUserBuilder")
        public Product(User user, String productName, int productPrice, int productOptionNum){
           	this.user= user;
            this.productName = productName;
            this.productPrice = productPrice;
            this.productOptionNum  = productOptionNum;
           }  
        
        ์ดํ›„ ์ƒ์„ฑ์‹œ
        Product test = new Product
        		      .defaultBuilder()
        		      .user(user)
        		      .productCategory(productCategory)
        		      .productName(productName)
        		      .productOptionNum(productOptionNum)
        		      .build();
        						
        ํ˜น์€ category๊ฐ€ ์—†์„๋•Œ
        Product test2 = new Product
        			.ByUserBuilder()
        			.user(user)
        			.productName(productName)
        			.productOptionNum(productOptionNum)
        			.build();
        

RDS(Mysql) & ์ธํ…”๋ฆฌ์ œ์ด ์—ฐ๋™

<์ง„ํ–‰๊ณผ์ • >

  1. aws inbound ๊ทœ์น™-> ํ•ญ์ƒ ์œ„์น˜๋ฌด๊ด€ IP์„ค์ •์„ ํ•ด๋‘์ž!! (์ธ๋ฐ”์šด๋“œ๊ฐ€ ์„ค์ • ๋˜์–ด์žˆ์ง€ ์•Š์œผ๋ฉด ๋น„๋ฒˆ๊นŒ์ง€ ๋‹ค ์„ค์ •ํ•ด๋„ timeout ๋ฐœ์ƒ
  2. csv ํŒŒ์ผ mysql workbench read ๋ฌธ์ œ : ์ •ํ™•ํ•œ ์ด์œ ๋Š” ๋ชจ๋ฅด๊ฒ ์œผ๋‚˜ UTF8๋กœ ์„ค์ •ํ•ด๋„ ํ•œ๊ธ€์„ ์ฝ์ง€ ๋ชปํ•˜๋Š” ๊ฑธ๋กœ ํŒ๋‹จ
  3. 2์˜ ๋Œ€์•ˆ์œผ๋กœ jsonํŒŒ์ผ๋กœ ๋ณ€ํ˜•ํ•ด์„œ ์ €์žฅ-> json ํŒŒ์ผ๋กœ ๋ณ€ํ˜•ํ•˜๋ฉด ๋“ค์–ด๊ฐ -> ๋‹จ, varchar, int ๋“ฑ๋“ฑ ํƒ€์ž…์„ ์ œ๋Œ€๋กœ ๋งž์ถฐ์ฃผ์ง€ ์•Š์œผ๋ฉด import์„ฑ๊ณต ํ›„์— 0๊ฐœ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋‹ค๊ณ  ๋œธ
  4. json ํŒŒ์ผ๋กœ ๋ณ€ํ˜•ํ• ๋•Œ python์œผ๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ „์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.