Skip to content

suuu0719/springboot-study

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

17 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SpringBoot

2μž₯ MVC νŒ¨ν„΄ 이해와 μ‹€μŠ΅

MVCνŒ¨ν„΄ ν™œμš©ν•΄ λ·° ν…œν”Œλ¦Ώ νŽ˜μ΄μ§€ λ§Œλ“€κΈ°

μŠ€ν¬λ¦°μƒ· 2024-01-03 210123

mustache(λ·°)

λ·°ν…œν”Œλ¦Ώμ„ λ§Œλ“œλŠ” 도ꡬ=λ·° ν…œν”Œλ¦Ώ μ—”μ§„

src > main > resources > templates에 λ¨ΈμŠ€ν…ŒμΉ˜ 파일 μ €μž₯ν•˜λ©΄ μŠ€ν”„λ§λΆ€νŠΈμ—μ„œ μžλ™μœΌλ‘œ λ‘œλ”©

제일 μœ—μ€„μ— doc μž…λ ₯ ν›„ tabν‚€ λˆ„λ₯΄λ©΄ κΈ°λ³Έ htmlμ½”λ“œ μžλ™μœΌλ‘œ μž‘μ„±λ¨

이 νŽ˜μ΄μ§€λ₯Ό μ›Ήμ—μ„œ 보렀면 컨트둀러, λͺ¨λΈ μ΄μš©ν•΄μ•Όν•¨

컨트둀러

src > main > javaλ””λ ‰ν„°λ¦¬μ˜ κΈ°λ³Έ νŒ¨ν‚€μ§€ μ•ˆμ— 컨트둀러 νŒ¨ν‚€μ§€λ‘œ 생성

기본으둜 μž…λ ₯된 νŒ¨ν‚€μ§€λͺ… 뒀에 controller μΆ”κ°€, ν•΄λ‹Ή νŒ¨ν‚€μ§€ μ•ˆμ— ###Controller Class 생성

이 ν΄λž˜μŠ€κ°€ μ»¨νŠΈλ‘€λŸ¬μž„μ„ μ„ μ–Έν•˜λŠ” @Controller μ–΄λ…Έν…Œμ΄μ…˜ μž‘μ„±, λ·° λ°˜ν™˜ν•  λ©”μ„œλ“œ 생성 ν›„ returnλ¬Έ μ•ˆμ— mustache νŽ˜μ΄μ§€ λ°˜ν™˜

Controller Classμ•ˆμ— @GetMapping 으둜 URLμ£Όμ†Œ λ°˜ν™˜

λͺ¨λΈ μΆ”κ°€

컨트둀러 λ©”μ„œλ“œμ˜ λ§€κ°œλ³€μˆ˜λ‘œ λ°›μ•„μ˜΄

λ·°ν…œν”Œλ¦Ώμ— λ³€μˆ˜ μ‚½μž… {{λ³€μˆ˜λͺ…}}

λ·° λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œμ— Model νƒ€μž…μ˜ model λ§€κ°œλ³€μˆ˜ μΆ”κ°€

model.addAttribute("λ³€μˆ˜λͺ…", "λ³€μˆ«κ°’") : λͺ¨λΈμ—μ„œ λ³€μˆ˜λ₯Ό λ“±λ‘ν•˜λŠ” λ©”μ„œλ“œ

MVC νŒ¨ν„΄ μ‹€μŠ΅ μš”μ•½

image

3μž₯ κ²Œμ‹œνŒ λ§Œλ“€κ³  μƒˆ κΈ€ μž‘μ„±ν•˜κΈ°: Create

3.1 νΌλ°μ΄ν„°λž€

폼데이터: html μš”μ†ŒμΈ < form>νƒœκ·Έμ— μ‹€λ € μ „μ†‘λ˜λŠ” 데이터

<form> νƒœκ·Έ : μ›Ήν”„λΌμš°μ €μ—μ„œ μ„œλ²„λ‘œ 데이터λ₯Ό 전솑할 λ•Œ μ‚¬μš©, 데이터λ₯Ό μ „μ†‘ν• λ•Œ μ–΄λ””λ‘œ, μ–΄λ–»κ²Œ 보낼지 등을 μ μ–΄μ„œ 보냄 < form>νƒœκ·Έμ— μ‹€μ–΄ 보낸 λ°μ΄ν„°λŠ” μ„œλ²„μ˜ μ»¨νŠΈλ‘€λŸ¬κ°€ 객체에 λ‹΄μ•„μ„œ λ°›μŒ

DTO: data transfer object, < form>νƒœκ·Έμ— 싀어보낸 데이터λ₯Ό λ‹΄μ•„ λ°›λŠ” μ„œλ²„ 컨트둀러의 객체, DTO둜 받은 λ°μ΄ν„°λŠ” μ΅œμ’…μ μœΌλ‘œ λ°μ΄ν„°νŽ˜μ΄μŠ€μ— μ €μž₯됨

3.2 폼데이터 DTO둜 λ°›κΈ°

<form>νƒœκ·Έ 속성

  • action: μ–΄λ””λ‘œ 보낼지에 κ΄€ν•œ 정보, URL μ—°κ²° μ£Όμ†Œ. ex) action="/articles/create" ν•΄λ‹Ή νŽ˜μ΄μ§€λ‘œ 폼데이터λ₯Ό λ³΄λ‚Έλ‹€λŠ” 의미
  • method: μ–΄λ–»κ²Œ 보낼지에 κ΄€ν•œ 정보, μ†μ„±κ°’μœΌλ‘œ get, post 2κ°€μ§€ μ„€μ • κ°€λŠ₯.

폼데이터 λ°›κΈ°

λ·°νŽ˜μ΄μ§€μ—μ„œ 폼데이터λ₯Ό postλ°©μ‹μœΌλ‘œ μ „μ†‘ν•˜λ―€λ‘œ μ»¨νŠΈλ‘€λŸ¬μ—μ„œ λ°›μ„λ•Œλ„ @PostMapping()으둜 λ°›μŒ.

DTO λ§Œλ“€κΈ°

ν”„λ‘œμ νŠΈ νŒŒμΌμ—μ„œ newβ†’package 생성 ν›„ dto νŒ¨ν‚€μ§€ 생성 (μ»¨νŠΈλ‘€λŸ¬μ™€ 같은 레벨)

μƒˆλ‘œμš΄ μžλ°” 클래슀 생성 ν•˜λ©΄ ν•΄λ‹Ή μžλ°”νŒŒμΌμ΄ 폼 데이터λ₯Ό λ°›μ•„μ˜¬ 그릇, DTOκ°€ 됨.

μž…λ ₯λ°›λŠ” 창만큼의 ν•„λ“œ 갯수 ν•„μš”. μƒμ„±μžμ™€ toString λ©”μ„œλ“œ μΆ”κ°€

폼데이터 DTO에 λ‹΄κΈ°

DTO클래슀λ₯Ό 컨트둀러 λ©”μ„œλ“œμ˜ λ§€κ°œλ³€μˆ˜λ‘œ λ°›μ•„μ˜΄, form 객체λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ μ„ μ–Έ

μž…λ ₯폼과 DTO ν•„λ“œ μ—°κ²°

mustache μž…λ ₯폼에 ν•„λ“œλͺ… μ§€μ •ν•˜λ©΄ ν•΄λ‹Ή μž…λ ₯폼이 DTO의 ν•„λ“œμ™€ 연결됨

μš”μ•½

  1. λ·°νŽ˜μ΄μ§€ λ§Œλ“€κΈ° (form action, method μ§€μ •)
  2. 컨트둀러 λ§Œλ“€κΈ° (PostMapping으둜 URL μ£Όμ†Œ μ—°κ²°)
  3. DTO λ§Œλ“€κΈ°
  4. μ»¨νŠΈλ‘€λŸ¬μ—μ„œ 폼 데이터 전솑받아 DTO 객체에 λ‹΄κΈ°

3.3 DTOλ₯Ό 데이터 λ² μ΄μŠ€μ— μ €μž₯ν•˜κΈ°

λ°μ΄ν„°λ² μ΄μŠ€: 데이터λ₯Ό κ΄€λ¦¬ν•˜λŠ” μ°½κ³ , DB의 λͺ¨λ“  λ°μ΄ν„°λŠ” ν–‰κ³Ό μ—΄λ‘œ κ΅¬μ„±λœ ν…Œμ΄λΈ”μ— μ €μž₯ν•΄ 관리

JPA: μžλ°” μ–Έμ–΄λ‘œ DB에 λͺ…령을 λ‚΄λ¦¬λŠ” 도ꡬ, 데이터λ₯Ό 객체 μ§€ν–₯적으둜 관리할 수 있게 ν•΄μ€Œ

μ—”ν‹°ν‹°: μžλ°” 객체가 DBλ₯Ό 이해할 수 있게 λ§Œλ“  것, 이λ₯Ό 기반으둜 ν…Œμ΄λΈ”μ΄ λ§Œλ“€μ–΄μ§

λ¦¬νŒŒμ§€ν„°λ¦¬: μ—”ν‹°ν‹°κ°€ DB속 ν…Œμ΄λΈ”μ— μ €μž₯ 및 관리될 수 있게 ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€

폼 데이터λ₯Ό DB에 μ €μž₯ν•˜λ €λ©΄

  1. DTOλ₯Ό μ—”ν‹°ν‹°λ‘œ λ³€ν™˜ν•˜κΈ°
  2. λ¦¬νŒŒμ§€ν„°λ¦¬λ₯Ό μ΄μš©ν•΄ μ—”ν‹°ν‹°λ₯Ό DB에 μ €μž₯ν•˜κΈ°

DTOλ₯Ό μ—”ν‹°ν‹°λ‘œ λ³€ν™˜ν•˜κΈ°

Article article = form.toEntity(); // form 객체의 toEntity() λ©”μ„œλ“œ 호좜, κ·Έ λ°˜ν™˜ 값을 Article νƒ€μž…μ˜ article 엔티티에 μ €μž₯

Article 클래슀 λ§Œλ“€κΈ°: ν”„λ‘œμ νŠΈμ— entity νŒ¨ν‚€μ§€ λ§Œλ“  ν›„ 클래슀 생성

  1. @Entity μ–΄λ…Έν…Œμ΄μ…˜ 뢙이기
  2. @Column μ–΄λ…Έν…Œμ΄μ…˜ 뢙이고 ν•„λ“œ 생성
  3. λŒ€ν‘―κ°’ @Id둜 μ„ μ–Έ ν›„ @GeneratedValue둜 λŒ€ν‘―κ°’ μžλ™ 생성 -> λŒ€ν‘―κ°’μœΌλ‘œ μ€‘λ³΅λœ 데이터 μžˆλ”λΌλ„ ꡬ뢄 κ°€λŠ₯
  4. μƒμ„±μžμ™€ toString() λ©”μ„œλ“œ 생성

toEntity()λ©”μ„œλ“œ 생성: DTO인 form 객체λ₯Ό μ—”ν‹°ν‹° 객체둜 λ³€ν™˜ν•˜λŠ” μ—­ν• 

  1. ArticleForm (DTO 클래슀)에 toEntity() λ©”μ„œλ“œ μΆ”κ°€
  2. DTO 객체 μ—”ν‹°ν‹°λ‘œ λ°˜ν™˜, return new Article(null, title, content); //id정보 μ œμ™Έν•œ ArticleForm 객체의 전달값 μž…λ ₯

λ¦¬νŒŒμ§€ν„°λ¦¬λ‘œ μ—”ν‹°ν‹°λ₯Ό DB에 μ €μž₯ν•˜κΈ°

  1. 컨트둀러 ν•„λ“œ 선언뢀에 λ¦¬νŒŒμ§€ν„°λ¦¬ 객체 μ„ μ–Έ
  2. Article saved = articleRepository.save(); // save() λ©”μ„œλ“œ ν˜ΈμΆœν•΄ article μ—”ν‹°ν‹° μ €μž₯. save() λ©”μ„œλ“œλŠ” μ €μž₯된 μ—”ν‹°ν‹°λ₯Ό λ°˜ν™˜ν•˜μ—¬ Article νƒ€μž…μ˜ savedλΌλŠ” 객체에 λ°›μ•„μ˜΄

λ¦¬νŒŒμ§€ν„°λ¦¬ λ§Œλ“€κΈ°: μΈν„°νŽ˜μ΄μŠ€ 생성

  1. ν”„λ‘œμ νŠΈμ— repository νŒ¨ν‚€μ§€ 생성, ArticleRepository μΈν„°νŽ˜μ΄μŠ€ 생성
  2. JPAμ—μ„œ μ œκ³΅ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€ ν™œμš©. λ¦¬νŒŒμ§€ν„°λ¦¬ 이름 뒀에extend CrudRepository<T, ID> 선택, <> μ•ˆμ— 2개의 μ œλ„€λ¦­ μš”μ†Œλ₯Ό λ°›μŒ
    1. Article: 관리 λŒ€μƒ μ—”ν‹°ν‹°μ˜ 클래슀 νƒ€μž…, μ—¬κΈ°μ„œλŠ” Article
    2. Long: 관리 λŒ€μƒ μ—”ν‹°ν‹°μ˜ λŒ€ν‘―κ°’ νƒ€μž…. idκ°€ λŒ€ν‘―κ°’μ΄λ―€λ‘œ Longνƒ€μž… μž…λ ₯

객체 μ£Όμž…ν•˜κΈ°: μŠ€ν”„λ§ λΆ€νŠΈλŠ” 객체λ₯Ό λ§Œλ“€μ§€ μ•Šμ•„λ„ 미리 생성해놓은 객체 κ°€μ Έλ‹€ μ—°κ²°ν•΄μ„œ μ‚¬μš© κ°€λŠ₯

μ˜μ‘΄μ„± μ£Όμž…(DI): 컨트둀러 ν΄λž˜μŠ€μ— @AutoWired μ–΄λ…Έν…Œμ΄μ…˜ 뢙이면 μŠ€ν”„λ§λΆ€νŠΈκ°€ λ§Œλ“€μ–΄λ†“μ€ 객체 가져와 μ£Όμž…

3.4 DB데이터 μ£Όμž…ν•˜κΈ°

CRUD

create: 생성 read : 쑰회 update : μˆ˜μ • delete : μ‚­μ œ, CRUD μ‘°μž‘μ€ SQL둜 μˆ˜ν–‰

H2 DB μ ‘μ†ν•˜κΈ°

src > main > resources > application.properties에 spring.h2.console.enabled=true μž‘μ„±

localhost:8080/h2-console 접속, RUN νƒ­μ—μ„œ jdbc μ£Όμ†Œ 찾은 ν›„ JDBC URL에 λΆ™μ—¬ λ„£κ³  Connect

SELECTλ¬Έ: ν…Œμ΄λΈ”μ˜ 속성 쑰회

SELECT 속성λͺ… FROM ν…Œμ΄λΈ”λͺ…;

속성λͺ… λŒ€μ‹  * μ‚¬μš© μ‹œ λͺ¨λ“  속성을 μ‘°νšŒν•˜λΌλŠ” 뜻

INSERTλ¬Έ: ν…Œμ΄λΈ”μ— 데이터(λ ˆμ½”λ“œ) 직접 μ‚½μž…

INSERT INTO ν…Œμ΄λΈ”λͺ…(속셩λͺ…1, 속성λͺ…2, 속성λͺ…3, ...) VALUES (κ°’1, κ°’2, κ°’3, ...);

4μž₯ 둬볡과 λ¦¬νŒ©ν„°λ§

4.1 λ‘¬λ³΅μ΄λž€

둬볡: μ½”λ“œλ₯Ό κ°„μ†Œν™”ν•΄μ£ΌλŠ” 라이브러리, ν•„μˆ˜ μ½›λ₯Ό κ°„νŽΈν•˜κ²Œ μž‘μ„±ν•  수 있음

λ‘œκΉ…: ν”„λ‘œκ·Έλž¨μ˜ μˆ˜ν–‰ 과정을 기둝으둜 λ‚¨κΈ°λŠ” 것

λ¦¬νŒ©ν„°λ§: μ½”λ“œμ˜ κΈ°λŠ₯μ—λŠ” 변함없이 μ½”λ“œμ˜ ꡬ쑰 λ˜λŠ” μ„±λŠ₯을 κ°œμ„ ν•˜λŠ” μž‘μ—…

4.2 둬볡을 ν™œμš©ν•΄ λ¦¬νŒ©ν„°λ§ν•˜κΈ°

@AllArgsContructor: μƒμ„±μž μ–΄λ…Έν…Œμ΄μ…˜

@ToString: toString() λ©”μ†Œλ“œ μ–΄λ…Έν…Œμ΄μ…˜

@Slf4j: λ‘œκΉ…μ„ μœ„ν•œ μ–΄λ…Έν…Œμ΄μ…˜, Simple Logging Facade for Java의 μ•½μž

log.info(): μ»¨νŠΈλ‘€λŸ¬μ— printλ¬Έ λŒ€μ‹  둜그 남기기 μœ„ν•΄ μ‚¬μš©

5μž₯ κ²Œμ‹œκΈ€ 읽기: Read

5.1 데이터 쑰회 κ³Όμ •

KakaoTalk_20240113_170302554

5.2 단일 데이터 쑰회

@GetMapping("/articles/{id}")
public String show(@PathVariable Long id, Model model){
        log.info("id =" + id); // idλ₯Ό 잘 λ°›μ•˜λŠ”μ§€ ν™•μΈν•˜λŠ” 둜그 찍기
        // 1. idλ₯Ό μ‘°νšŒν•΄ 데이터 κ°€μ Έμ˜€κΈ°
        Article articleEntity = articleRepository.findById(id).orElse(null); //.orElse(null) λΆ™νžˆμ§€ μ•Šκ³  Article λŒ€μ‹  Optional<Article> 넣어도 됨
        // 2. λͺ¨λΈμ— 데이터 λ“±λ‘ν•˜κΈ°
        model.addAttribute("article", articleEntity); // articleμ΄λΌλŠ” μ΄λ¦„μœΌλ‘œ articleEntity 객체 등둝
        // 3. λ·° νŽ˜μ΄μ§€ λ°˜ν™˜ν•˜κΈ°
        return "articles/show";
        }
{{>layouts/header}}
<table class="table">
    <thead>
    <tr>
        <th scope="col">Id</th>
        <th scope="col">Title</th>
        <th scope="col">Content</th>
    </tr>
    </thead>
    <tbody>
    {{#article}}
    <tr>
        <th>{{id}}</th>
        <td>{{title}}</td>
        <td>{{content}}</td>
    </tr>
    {{/article}}
    </tbody>
</table>
{{>layouts/footer}}

5.3 데이터 λͺ©λ‘ 쑰회

@GetMapping("/articles")
    public String index(Model model) {
        // 1. λͺ¨λ“  데이터 κ°€μ Έμ˜€κΈ°
        List<Article> articleEntityList = articleRepository.findAll();
        // 2. λͺ¨λΈμ— 데이터 λ“±λ‘ν•˜κΈ°
        model.addAttribute("articleList", articleEntityList);
        // 3. λ·° νŽ˜μ΄μ§€ μ„€μ •ν•˜κΈ°
        return "articles/index";
    }
{{>layouts/header}}
<table class="table">
    <thead>
    <tr>
        <th scope="col">Id</th>
        <th scope="col">Title</th>
        <th scope="col">Content</th>
    </tr>
    </thead>
    <tbody>
    {{#articleList}}
        <tr>
            <th>{{id}}</th>
            <td>{{title}}</td>
            <td>{{content}}</td>
        </tr>
    {{/articleList}}
    </tbody>
</table>
{{>layouts/footer}}

6μž₯ κ²Œμ‹œνŒ λ‚΄ νŽ˜μ΄μ§€ μ΄λ™ν•˜κΈ°

링크: 미리 μ •ν•΄ 놓은 μš”μ²­μ„ κ°„νŽΈν•˜κ²Œ μ „μ†‘ν•˜λŠ” κΈ°λŠ₯, νŽ˜μ΄μ§€ 이동을 μœ„ν•΄ μ‚¬μš©. HTML의 <a> νƒœκ·Έ ν˜Ήμ€ <form> νƒœκ·Έλ‘œ μž‘μ„±, ν΄λΌμ΄μ–ΈνŠΈκ°€ 링크λ₯Ό 톡해 μ–΄λŠ νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•˜κ² λ‹€κ³  μš”μ²­ν•˜λ©΄ μ„œλ²„λŠ” κ²°κ³Ό νŽ˜μ΄μ§€λ₯Ό 응닡

λ¦¬λ‹€μ΄λ ‰νŠΈ: ν΄λΌμ΄μ–ΈνŠΈκ°€ 보낸 μš”μ²­μ„ 마친 ν›„ κ³„μ†ν•΄μ„œ μ²˜λ¦¬ν•  λ‹€μŒ μš”μ²­ μ£Όμ†Œλ₯Ό μž¬μ§€μ‹œ, λ¦¬λ‹€μ΄λ ‰νŠΈλ₯Ό μ§€μ‹œλ°›μ€ ν΄λΌμ΄μ–ΈνŠΈλŠ” ν•΄λ‹Ή μ£Όμ†Œλ‘œ λ‹€μ‹œ μš”μ²­μ„ 보내고 μ„œλ²„λŠ” 이에 λŒ€ν•œ κ²°κ³Όλ₯Ό 응닡

λ·° νŒŒμΌμ— 링크 κ±ΈκΈ°: <a> νƒœκ·Έ μ΄μš©ν•΄ λ‹€μŒκ³Ό 같은 ν˜•μ‹μœΌλ‘œ μž‘μ„±

<a href="URL μ£Όμ†Œ">링크λ₯Ό κ±Έ λŒ€μƒ</a>

λ¦¬λ‹€μ΄λ ‰νŠΈ μ •μ˜ν•˜κΈ°: return문을 μ‚¬μš©ν•΄ λ‹€μŒκ³Ό 같은 ν˜•μ‹μœΌλ‘œ μž‘μ„±

return "redirect:URL_μ£Όμ†Œ";

7μž₯ κ²Œμ‹œκΈ€ μˆ˜μ •ν•˜κΈ°: Update

7.1 데이터 μˆ˜μ • κ³Όμ •

  1. <μˆ˜μ • νŽ˜μ΄μ§€> λ§Œλ“€κ³  κΈ°μ‘΄ 데이터 뢈러였기
  2. 데이터λ₯Ό μˆ˜μ •ν•΄ DB에 λ°˜μ˜ν•œ ν›„ κ²°κ³Όλ₯Ό λ³Ό 수 있게 <μƒμ„ΈνŽ˜μ΄μ§€>둜 λ¦¬λ‹€μ΄λ ‰νŠΈν•˜κΈ°

7.2 <μˆ˜μ • νŽ˜μ΄μ§€ λ§Œλ“€κΈ°>

@GetMapping("/articles/{id}/edit")
    public String edit(@PathVariable Long id, Model model){
        // μˆ˜μ •ν•  데이터 κ°€μ Έμ˜€κΈ°
        Article articleEntity = articleRepository.findById(id).orElse(null);
        // λͺ¨λΈμ— 데이터 λ“±λ‘ν•˜κΈ°
        model.addAttribute("article", articleEntity);
        // λ·° νŽ˜μ΄μ§€ μ„€μ •ν•˜κΈ°
        return "articles/edit";
    }
value="{{title}}" <!--데이터 뢈러였기, article.titleμ—μ„œ article μƒλž΅ κ°€λŠ₯-->
<a href="/articles/{{id}}">Back</a> <!--Back λ²„νŠΌ μ£Όμ†Œ μ„€μ •-->

μˆ˜μ • νŽ˜μ΄μ§€κ°€ λ‚˜μ˜¬ λ–„κΉŒμ§€ 처리 흐름

  1. ν΄λΌμ΄μ–ΈνŠΈλ‘œλΆ€ν„° 데이터 μˆ˜μ • μš”μ²­μ΄ λ“€μ–΄μ˜¨λ‹€
  2. μˆ˜μ • μš”μ²­ 데이터λ₯Ό DBμ—μ„œ μ°ΎλŠ”λ‹€
  3. μˆ˜μ • μš”μ²­ 데이터λ₯Ό λͺ¨λΈμ— λ“±λ‘ν•œλ‹€
  4. λ·°νŽ˜μ΄μ§€μ— μˆ˜μ •ν•  데이터λ₯Ό ν•¨κ»˜ 보여쀀닀

7.3 μˆ˜μ • 데이터λ₯Ό DB에 κ°±μ‹ ν•˜κΈ°

MVC:μ„œλ²„ 역할을 λΆ„λ‹΄ν•΄ μ²˜λ¦¬ν•˜λŠ” 기법

JPA:μ„œλ²„μ™€ DB κ°„ μ†Œν†΅μ— κ΄€μ—¬ν•˜λŠ” 기술

SQL:DB 데이터λ₯Ό κ΄€λ¦¬ν•˜λŠ” μ–Έμ–΄

HTTP:데이터λ₯Ό μ£Όκ³ λ°›κΈ° μœ„ν•œ 톡신 κ·œμ•½

ν”„λ‘œν† μ½œ: 컴퓨터 간에 μ›ν™œν•˜κ²Œ ν†΅μ‹ ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•˜λŠ” μ „ 세계 ν‘œμ€€μ–Έμ–΄ img.png

New -> File -> data.sql μ„œλ²„λ₯Ό 껐닀 μΌ€λ•Œλ§ˆλ‹€ 데이터 μžλ™μœΌλ‘œ μ‚½μž…λ¨

INSERT INTO article(id, title, content) VALUES (1, 'κ°€κ°€κ°€κ°€', '1111');
INSERT INTO article(id, title, content) VALUES (2, 'λ‚˜λ‚˜λ‚˜λ‚˜', '2222');
INSERT INTO article(id, title, content) VALUES (3, 'λ‹€λ‹€λ‹€λ‹€', '3333');

edit.mustache: action 속성은 폼데이터λ₯Ό μ–΄λ””λ‘œ 보낼지 URL μ§€μ •, method 속성은 μ–΄λ–»κ²Œ 보낼지 방식 μ§€μ •, idλŠ” λͺ‡λ²ˆ article을 μˆ˜μ •ν•˜λŠ”μ§€ μ•Œλ €μ€˜μ•Όν•¨

<form class="container" action="/articles/update" method="post">
    <input name="id" type="hidden" value="{{id}}">
@PostMapping("/articles/update")
    public String update(ArticleForm form){ // λ§€κ°œλ³€μˆ˜λ‘œ DTO λ°›μ•„μ˜€κΈ°
        log.info(form.toString());
        // 1. DTOλ₯Ό μ—”ν‹°ν‹°λ‘œ λ³€ν™˜ν•˜κΈ°
        Article articleEntity = form.toEntity();
        log.info(articleEntity.toString());
        // 2. μ—”ν‹°ν‹°λ₯Ό DB에 μ €μž₯
        // 2-1. DBμ—μ„œ κΈ°μ‘΄ 데이터 κ°€μ Έμ˜€κΈ°
        Article target = articleRepository.findById(articleEntity.getId()).orElse(null);
        // 2-2. κΈ°μ‘΄ 데이터 값을 κ°±μ‹ ν•˜κΈ°
        if (target!=null) {
            articleRepository.save(articleEntity); // μ—”ν‹°ν‹°λ₯Ό DB μ €μž₯ (κ°±μ‹ )
        }
        // 3. μˆ˜μ • κ²°κ³Ό νŽ˜μ΄μ§€λ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈ
        return "redirect:/articles/" + articleEntity.getId();
    }

sqlμ—μ„œ 직접 DB κ°±μ‹ 

UPDATE ν…Œμ΄λΈ”λͺ… SET 속성λͺ…=λ³€κ²½ν• _κ°’ WHERE 쑰건 ;
UPDATE article SET title='κ°€κ°€κ°€', content='λ‚˜λ‚˜λ‚˜' where id='2';
SELECT * FROM article 

[μŠ€ν”„λ§ λΆ€νŠΈ μž…λ¬Έ 16] 데아ᄐα…₯ α„‰α…‘α†¨α„Œα…¦α„’α…‘α„€α…΅

RedirectAttributes: RedirectAttributes 객체의 addFlashAttribute() λ©”μ„œλ“œλŠ” λ¦¬λ‹€μ΄λ ‰νŠΈλœ νŽ˜μ΄μ§€μ—μ„œ μ‚¬μš©ν•  μΌνšŒμ„± 데이터λ₯Ό 등둝할 수 있음 img_1.png

@GetMapping("/articles/{id}/delete")
    public String delete(@PathVariable Long id, RedirectAttributes rttr){ // idλ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ κ°€μ Έμ˜€κΈ°
        log.info("μ‚­μ œ μš”μ²­μ΄ λ“€μ–΄μ™”μŠ΅λ‹ˆλ‹€!!");
        // 1. μ‚­μ œν•  λŒ€μƒ κ°€μ Έμ˜€κΈ°
        Article target = articleRepository.findById(id).orElse(null); // 데이터 μ°ΎκΈ°
        log.info(target.toString());
        // 2. λŒ€μƒ μ—”ν‹°ν‹° μ‚­μ œν•˜κΈ°
        if (target!=null) {
            articleRepository.delete(target);
            rttr.addFlashAttribute("msg", "μ‚­μ œλμŠ΅λ‹ˆλ‹€!");
        }
        // 3. κ²°κ³Ό νŽ˜μ΄μ§€λ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈν•˜κΈ°
        return "redirect:/articles";
    }

addFlashAttribute() λ©”μ„œλ“œ: λ¦¬λ‹€μ΄λ ‰νŠΈ μ‹œμ μ— ν•œλ²ˆλ§Œ μ‚¬μš©ν•  데이터 등둝할 수 있음 (ν•œλ²ˆ μ“°κ³  μ‚¬λΌμ§€λŠ” νœ˜λ°œμ„± 데이터λ₯Ό 등둝)

객체λͺ….addFlashAttribute(λ„˜κ²¨μ£Όλ €λŠ”_ν‚€_λ¬Έμžμ—΄, λ„˜κ²¨μ£Όλ €λŠ”_κ°’_객체);

μ‚­μ œ λ©”μ‹œμ§€ ν‘œμ‹œ

header.mustache

{{#msg}}
    <div class="alert alert-primary alert-dismissible">
        {{msg}}
        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
    </div>
{{/msg}}

SQLμ—μ„œ 직접 μ‚­μ œ

DELETE [FROM] ν…Œμ΄λΈ”λͺ… WHERE 쑰건; --[]:μƒλž΅κ°€λŠ₯
DELETE article WHERE id=2;

[μŠ€ν”„λ§ λΆ€νŠΈ μž…λ¬Έ 17] CRUDα„‹α…ͺ SQL 쿼라

쿼리: DB에 정보λ₯Ό μš”μ²­ν•˜λŠ” ꡬ문

λ‘œκΉ…: μ‹œμŠ€ν…œμ΄ μž‘λ™ν•  λ•Œ λ‹Ήμ‹œμ˜ μƒνƒœμ™€ μž‘λ™ 정보λ₯Ό κΈ°λ‘ν•˜λŠ” 것

JPA λ‘œκΉ… μ„€μ •-application.properties

# 17κ°•: JPA λ‘œκΉ… μ„€μ •
## 디버그 레벨둜 쿼리 좜λ ₯
logging.level.org.hibernate.SQL=DEBUG
## 이쁘게 보여주기
spring.jpa.properties.hibernate.format_sql=true
## νŒŒλΌλ―Έν„° 보여주기
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
## κ³ μ • url μ„€μ •
spring.datasource.url=jdbc:h2:mem:testdb

insert문

insert 
    into
        article
        (content,title,id) 
    values
        (?,?,default)

select문

select
        a1_0.id,
        a1_0.content,
        a1_0.title 
    from
        article a1_0
select
        a1_0.id,
        a1_0.content,
        a1_0.title 
    from
        article a1_0 
    where
        a1_0.id=?

전체가 μ•„λ‹Œ ν•œκ°€μ§€λ§Œ μ„ νƒν•œ κ²½μš°β†’ where

update문

update
        article 
    set
        content=?,
        title=? 
    where
        id=?

delete문

delete 
    from
        article 
    where
        id=?

tableλ§Œλ“œλŠ” 쿼리문

create table article (
        id bigint generated by default as identity,
        content varchar(255),
        title varchar(255),
        primary key (id)
    )

@GeneratedValue(strategy = GenerationType.*IDENTITY*) // DBκ°€ idλ₯Ό μžλ™ 생성 μ–΄λ…Έν…Œμ΄μ…˜, id값이 κ²ΉμΉ˜μ§€ μ•Šλ„λ‘ 함

[μŠ€ν”„λ§ λΆ€νŠΈ μž…λ¬Έ 18] RestAPIα„‹α…ͺ JSON

REST API: μ›Ήμ„œλ²„μ˜ μžμ›μ„ ν΄λΌμ΄μ–ΈνŠΈμ— ꡬ애받지 μ•Šκ³  μ‚¬μš©ν•  수 있게 ν•˜λŠ” 섀계 방식, httpλ₯Ό μ΄μš©ν•΄ μ„œλ²„μ˜ μžμ› λ°˜ν™˜. μ΄λ•Œ μ„œλ²„μ—μ„œ λ³΄λ‚΄λŠ” 응닡은 νŠΉμ • 기기에 μ’…μ†λ˜μ§€ μ•Šλ„λ‘ λͺ¨λ“  κΈ°κΈ°μ—μ„œ ν†΅ν•˜λŠ” 데이터λ₯Ό λ°˜ν™˜

JSON: μžλ°”μŠ€ν¬λ¦½νŠΈλ₯Ό μ΄μš©ν•œ 객체 ν‘œν˜„μ‹, REST API의 응닡 데이터

HTTP μƒνƒœμ½”λ“œ

μƒνƒœμ½”λ“œ μ„€λͺ…
1XX (정보) μš”μ²­μ΄ μˆ˜μ‹ λΌ 처리 μ€‘μž…λ‹ˆλ‹€
2XX (성곡) μš”μ²­μ΄ μ •μƒμ μœΌλ‘œ μ²˜λ¦¬λμŠ΅λ‹ˆλ‹€
3XX (λ¦¬λ‹€μ΄λ ‰μ…˜ λ©”μ„Έμ§€) μš”μ²­μ„ μ™„λ£Œν•˜λ €λ©΄ μΆ”κ°€ 행동이 ν•„μš”ν•©λ‹ˆλ‹€
4XX (ν΄λΌμ΄μ–ΈνŠΈ μš”μ²­ 였λ₯˜) ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λΌ μ„œλ²„κ°€ μš”μ²­μ„ μˆ˜ν–‰ν•  수 μ—†μŠ΅λ‹ˆλ‹€
5XX (μ„œλ²„ 응닡 였λ₯˜) μ„œλ²„ 내뢀에 μ—λŸ¬κ°€ λ°œμƒν•΄ ν΄λΌμ΄μ–ΈνŠΈ μš”μ²­μ— λŒ€ν•΄ 적절히 μˆ˜ν–‰ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€

200:응닡 성곡

404: 찾을 수 μ—†λŠ” νŽ˜μ΄μ§€ μš”μ²­

201: 데이터 생성 μ™„λ£Œ

500: μ„œλ²„ λ‚΄λΆ€ μ—λŸ¬ λ°œμƒ

HTTP μš”μ²­κ³Ό 응닡 ꡬ쑰

img_2.png

[μŠ€ν”„λ§ λΆ€νŠΈ μž…λ¬Έ 19] HTTP와 RestController

REST API μ£Όμ†Œ 섀계

img_3.png img_4.png @RestController: Rest API용 컨트둀러, JSON λ°˜ν™˜ β†’ 데이터λ₯Ό λ°˜ν™˜ν•œλ‹€ (일반 μ»¨νŠΈλ‘€λŸ¬λŠ” λ·°ν…œν”Œλ¦ΏνŽ˜μ΄μ§€λ₯Ό λ°˜ν™˜)

ν΄λΌμ΄μ–ΈνŠΈμ˜ 데이터 쑰회, 생성, μˆ˜μ •, μ‚­μ œ μš”μ²­μ„ HTTP λ©”μ„œλ“œμ— 맞게 각각 @GetMapping, @PostMapping, @PatchMapping, @DeleteMapping 으둜 λ°›μ•„ μ²˜λ¦¬ν•¨

@RequestBody : JSON 데이터 λ°›κΈ°

HttpStatus: Http μƒνƒœ μ½”λ“œλ₯Ό κ΄€λ¦¬ν•˜λŠ” 클래슀

ResponseEntity: Rest API μš”μ²­μ„ λ°›μ•„ μ‘λ‹΅ν• λ•Œ HTTP μƒνƒœ μ½”λ“œ, 헀더, 본문을 μ‹€μ–΄ λ³΄λ‚΄λŠ” 클래슀

  • 전체 μ½”λ“œ

    @Autowired // DI
        private ArticleRepository articleRepository;
    
        // Get
        @GetMapping("/api/articles")
        public List<Article> index() {
            return articleRepository.findAll();
        }
    
        @GetMapping("/api/articles/{id}")
        public Article show(@PathVariable Long id) {
            return articleRepository.findById(id).orElse(null);
        }
    
        // Post
        @PostMapping("api/articles")
        public Article create(@RequestBody ArticleForm dto) {
            Article article = dto.toEntity();
            return articleRepository.save(article);
        }
        // Patch
        @PatchMapping("api/articles/{id}")
        public ResponseEntity<Article> update(@PathVariable Long id, @RequestBody ArticleForm dto) {
            // 1. μˆ˜μ •μš© μ—”ν‹°ν‹° 생성
            Article article = dto.toEntity();
            log.info("id: {}, article: {}", id, article.toString());
            // 2. λŒ€μƒ μ—”ν‹°ν‹°λ₯Ό 쑰회
            Article target = articleRepository.findById(id).orElse(null);
            // 3. 잘λͺ»λœ μš”μ²­ 처리(λŒ€μƒμ΄ μ—†κ±°λ‚˜ idκ°€ λ‹€λ₯Έ 경우)
            if (target == null || id != article.getId()) {
                // 400, 잘λͺ»λœ μš”μ²­ 응닡
                log.info("잘λͺ»λœ μš”μ²­! id: {}, article: {}", id, article.toString());
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
    
            }
            // 4. μ—…λ°μ΄νŠΈ 및 정상 응닡(200)
            target.patch(article); // λ­”κ°€ μž‘μ„± μ•ˆν•˜κ³  μˆ˜μ •ν•  μ‹œ κ·Έ μ „ μƒνƒœ κ·ΈλŒ€λ‘œ μœ μ§€λ˜λ„λ‘
            Article updated = articleRepository.save(target);
    
            return ResponseEntity.status(HttpStatus.OK).body(updated);
        }
        // Delete
        @DeleteMapping("/api/articles/{id}")
        public ResponseEntity<Article> delete(@PathVariable Long id) {
            // λŒ€μƒ μ°ΎκΈ°
            Article target = articleRepository.findById(id).orElse(null);
            // 잘λͺ»λœ μš”μ²­ 처리
            if (target == null){
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
            }
            // λŒ€μƒ μ‚­μ œ
            articleRepository.delete(target);
            // 데이터 λ°˜ν™˜
            return ResponseEntity.status(HttpStatus.OK).build();
        }

[μŠ€ν”„λ§ λΆ€νŠΈ μž…λ¬Έ 20] ᄉα…₯바스 α„€α…¨α„Žα…³α†Όα„€α…ͺ α„α…³α„…α…’α†«α„Œα…’α†¨α„‰α…§α†«

μ„œλΉ„μŠ€: μ»¨νŠΈλ‘€λŸ¬μ™€ λ¦¬νŒŒμ§€ν„°λ¦¬ 사이에 μœ„μΉ˜ν•˜λŠ” 계측, 처리 μ—…λ¬΄μ˜ μˆœμ„œλ₯Ό 총괄 img_5.png νŠΈλžœμž­μ…˜: μ„œλΉ„μŠ€μ˜ 업무 μ²˜λ¦¬λŠ” νŠΈλžœμ μ…˜ λ‹¨μœ„λ‘œ 진행됨, λͺ¨λ‘ μ„±κ³΅ν•΄μ•Όν•˜λŠ” 일련의 과정을 λœ»ν•¨

λ‘€λ°±: νŠΈλžœμž­μ…˜ μ‹€νŒ¨ μ‹œ μ§„ν–‰ 초기 λ‹¨κ³„λ‘œ λŒλ¦¬λŠ” 것

@Service: ν•΄λ‹Ή μ–΄λ…Έν…Œμ΄μ…˜μ΄ μ„ μ–Έλœ 클래슀λ₯Ό μ„œλΉ„μŠ€λ‘œ 인식, μ„œλΉ„μŠ€ 객체 생성

@Transaction: ν•΄λ‹Ή μ–΄λ…Έν…Œμ΄μ…˜μ΄ μ„ μ–Έλœ λ©”μ„œλ“œλ₯Ό νŠΈλžœμž­μ…˜μœΌλ‘œ 묢음, μ΄λ ‡κ²Œ νŠΈλžœμž­μ…˜μœΌλ‘œ 묢인 λ©”μ„œλ“œλŠ” μ²˜μŒλΆ€ν„° λκΉŒμ§€ μ™„μ „νžˆ μ‹€ν–‰λ˜κ±°λ‚˜ μ•„μ˜ˆ μ‹€ν–‰λ˜μ§€ μ•Šκ±°λ‚˜ λ‘˜ 쀑 ν•˜λ‚˜λ‘œ λ™μž‘. 쀑간에 μ‹€νŒ¨ν•˜λ©΄ λ‘€λ°±ν•΄ μ²˜μŒμƒνƒœλ‘œ λ˜λŒμ•„κ°€κΈ° λ•Œλ¬Έ

  • 컨트둀러 κ°„λ‹¨ν•˜κ²Œ ν‘œν˜„ κ°€λŠ₯

    package com.example.firstproject.api;
    
    import com.example.firstproject.dto.ArticleForm;
    import com.example.firstproject.entity.Article;
    import com.example.firstproject.repository.ArticleRepository;
    import com.example.firstproject.service.ArticleService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    @Slf4j
    
    @RestController //RestAPI용 컨트둀러, 데이터(json)을 λ°˜ν™˜
    public class ArticleApiController {
        @Autowired // DI 생성, 객체λ₯Ό 가져와 μ—°κ²°
        private ArticleService articleService;
    
        // Get
        @GetMapping("/api/articles")
        public List<Article> index() {
            return articleService.index();
        }
    
        @GetMapping("/api/articles/{id}")
        public Article index(@PathVariable Long id) {
            return articleService.show(id);
        }
    
        // Post
        @PostMapping("api/articles")
        public ResponseEntity<Article> create(@RequestBody ArticleForm dto) {
            Article created = articleService.create(dto);
            return (created != null) ?
                    ResponseEntity.status(HttpStatus.OK).body(created) :
                    ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
        // Patch
        @PatchMapping("api/articles/{id}")
        public ResponseEntity<Article> update(@PathVariable Long id, @RequestBody ArticleForm dto) {
            Article updated = articleService.update(id, dto);
            return (updated != null) ?
                    ResponseEntity.status(HttpStatus.OK).body(updated) :
                    ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
        // Delete
        @DeleteMapping("/api/articles/{id}")
        public ResponseEntity<Article> delete(@PathVariable Long id) {
            Article deleted = articleService.delete(id);
            return (deleted!=null) ?
                    ResponseEntity.status(HttpStatus.OK).build() :
                    ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
    
        // νŠΈλžœμž­μ…˜ -> μ‹€νŒ¨ -> λ‘€λ°±!
        @PostMapping("/api/transaction-test")
        public ResponseEntity<List<Article>> transactionTest(@RequestBody List<ArticleForm> dtos) {
            List<Article> createdList = articleService.createArticles(dtos);
            return (createdList != null) ?
                    ResponseEntity.status(HttpStatus.OK).body(createdList) :
                    ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
    
    }
  • μ„œλΉ„μŠ€ 계측에 ν‘œν˜„

    package com.example.firstproject.service;
    
    import com.example.firstproject.dto.ArticleForm;
    import com.example.firstproject.entity.Article;
    import com.example.firstproject.repository.ArticleRepository;
    import jakarta.transaction.Transactional;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    import java.util.stream.Collectors;
    
    @Slf4j
    @Service // μ„œλΉ„μŠ€ μ„ μ–Έ(μ„œλΉ„μŠ€ 객체λ₯Ό μŠ€ν”„λ§λΆ€νŠΈμ— 생성)
    public class ArticleService {
    @Autowired
    private ArticleRepository articleRepository;
    
       public List<Article> index() {
           return articleRepository.findAll();
       }
    
       public Article show(Long id) {
           return  articleRepository.findById(id).orElse(null);
       }
    
       public Article create(ArticleForm dto) {
           Article article = dto.toEntity();
           if (article.getId()!=null) {
               return null;
           }
           return articleRepository.save(article);
       }
    
       public Article update(Long id, ArticleForm dto) {
           // 1. μˆ˜μ •μš© μ—”ν‹°ν‹° 생성
           Article article = dto.toEntity();
           log.info("id: {}, article: {}", id, article.toString());
           // 2. λŒ€μƒ μ—”ν‹°ν‹°λ₯Ό 쑰회
           Article target = articleRepository.findById(id).orElse(null);
           // 3. 잘λͺ»λœ μš”μ²­ 처리(λŒ€μƒμ΄ μ—†κ±°λ‚˜ idκ°€ λ‹€λ₯Έ 경우)
           if (target == null || id != article.getId()) {
               // 400, 잘λͺ»λœ μš”μ²­ 응닡
               log.info("잘λͺ»λœ μš”μ²­! id: {}, article: {}", id, article.toString());
               return null;
           }
           // 4. μ—…λ°μ΄νŠΈ
           target.patch(article); // λ­”κ°€ μž‘μ„± μ•ˆν•˜κ³  μˆ˜μ •ν•  μ‹œ κ·Έ μ „ μƒνƒœ κ·ΈλŒ€λ‘œ μœ μ§€λ˜λ„λ‘
           Article updated = articleRepository.save(target);
    
           return updated;
       }
    
       public Article delete(Long id) {
           // λŒ€μƒ μ°ΎκΈ°
           Article target = articleRepository.findById(id).orElse(null);
           // 잘λͺ»λœ μš”μ²­ 처리
           if (target == null){
               return null;
           }
           // λŒ€μƒ μ‚­μ œ ν›„ λ°˜ν™˜
           articleRepository.delete(target);
           return target;
       }
    
       @Transactional
       public List<Article> createArticles(List<ArticleForm> dtos) {
           // dto λ¬ΆμŒμ„ entity 묢음으둜 λ³€ν™˜
           List<Article> articleList = dtos.stream()
                   .map(dto -> dto.toEntity())
                   .collect(Collectors.toList());
           // entity λ¬ΆμŒμ„ DB둜 μ €μž₯
           articleList.stream()
                   .forEach(article -> articleRepository.save(article));
           // κ°•μ œ μ˜ˆμ™Έ λ°œμƒ
           articleRepository.findById(-1L).orElseThrow(
                   () -> new IllegalArgumentException("결제 μ‹€νŒ¨!")
           );
           // κ²°κ³Όκ°’ λ°˜ν™˜
            return articleList;
       }
    }

[μŠ€ν”„λ§ λΆ€νŠΈ μž…λ¬Έ 21] ν…ŒμŠ€νŠΈ μž‘μ„±ν•˜κΈ°

ν…ŒμŠ€νŠΈ:ν”„λ‘œκ·Έλž¨μ˜ ν’ˆμ§ˆ 검증을 μœ„ν•¨, μ˜λ„λŒ€λ‘œ ν”„λ‘œκ·Έλž¨μ΄ λ™μž‘ν•˜λŠ”μ§€ ν™•μΈν•˜λŠ” 것

ν…ŒμŠ€νŠΈμΌ€μ΄μŠ€:성곡과 μ‹€νŒ¨λ‘œ λ‚˜λ‰¨, 쑰건에 따라 λ‹€μ–‘ν•œ 경우둜 μž‘μ„±λ  수 있음

TDD:ν…ŒμŠ€νŠΈλ₯Ό ν†΅ν•œ μ½”λ“œ 검증과 λ¦¬νŒ©ν„°λ§μ„ 기반으둜 ν•œ 개발 방법둠인 ν…ŒμŠ€νŠΈ 주도 개발. ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό λ§Œλ“  ν›„ 이λ₯Ό ν†΅κ³Όν•˜λŠ” μ΅œμ†Œν•œμ˜ μ½”λ“œλΆ€ν„° μ‹œμž‘ν•΄ μ μ§„μ μœΌλ‘œ μ½”λ“œλ₯Ό κ°œμ„  및 ν™•μž₯ν•΄λ‚˜κ°€λŠ” κ°œλ°œλ°©μ‹ img_6.png img_7.png μ„±κ³΅ν•˜λ©΄ λ¦¬νŒ©ν„°λ§, μ‹€νŒ¨ν•˜λ©΄ 디버깅

@SpringBootTest: μŠ€ν”„λ§ λΆ€νŠΈ ν™˜κ²½κ³Ό μ—°λ™λœ ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ μ–΄λ…Έν…Œμ΄μ…˜

@Transactional:데이터 쑰회 μ™Έμ˜ 경우(생성, λ³€κ²½, μ‚­μ œ)μ—λŠ” νŠΈλžœμž­μ…˜ 처리λ₯Ό 톡해 λ‘€λ°±ν•˜λ„λ‘ ν•΄μ•Ό 함

[μŠ€ν”„λ§ λΆ€νŠΈ μž…λ¬Έ 22] ᄃᅒᆺ글 엔타타ᄋα…ͺ α„…α…΅α„‘α…‘α„Œα…΅α„α…₯α„…α…΅(feat. ν…ŒμŠ€νŠΈ)

μŠ€ν¬λ¦°μƒ· 2024-02-14 μ˜€ν›„ 11.42.44.png μΌλŒ€λ‹€ 관계:ν•˜λ‚˜μ˜ κ²Œμ‹œκΈ€μ— μˆ˜λ§Žμ€ λŒ“κΈ€μ΄ 달림 μŠ€ν¬λ¦°μƒ· 2024-02-14 μ˜€ν›„ 11.43.32.png λ‹€λŒ€μΌ 관계:μ—¬λŸ¬ λŒ“κΈ€μ΄ ν•˜λ‚˜μ˜ κ°œμ‹œκΈ€μ— 달림

PK, λŒ€ν‘œν‚€:id와 같이 μžμ‹ μ„ λŒ€ν‘œν•˜λŠ” 속성

FK, μ™Έλž˜ν‚€:μ—°κ΄€ λŒ€μƒμ„ κ°€λ¦¬ν‚€λŠ” 속성 (ν•΄λ‹Ή λŒ“κΈ€μ΄ μ–΄λ–€ κ²Œμ‹œκΈ€μ— 달린 것인지 μ•Œ 수 있음) μŠ€ν¬λ¦°μƒ· 2024-02-14 μ˜€ν›„ 11.48.04.png

λ„€μ΄ν‹°λΈŒ 쿼리 λ©”μ„œλ“œ: 쿼리λ₯Ό λ©”μ„œλ“œλ‘œ μž‘μ„±, 직접 μž‘μ„±ν•œ SQL쿼리λ₯Ό λ¦¬νŒŒμ§€ν„°λ¦¬ λ©”μ„œλ“œλ‘œ μ‹€ν–‰ν•  수 있게 ν•΄μ€Œ

  1. @Queryμ–΄λ…Έν…Œμ΄μ…˜ 이용 -> νŠΉμ • κ²Œμ‹œκΈ€μ˜ λͺ¨λ“  λŒ“κΈ€ μ‘°νšŒμ— μ‚¬μš©
@Query(value =
            "SELECT * " +
                    "FROM comment " +
                    "WHERE article_id = :articleId",
            nativeQuery = true)
    List<Comment> findbyArticleId(Long articleID);
  1. orm.xml 파일 이용 -> νŠΉμ • λ‹‰λ„€μž„μ˜ λͺ¨λ“  λŒ“κΈ€ μ‘°νšŒμ— μ‚¬μš©

@DataJpaTest: JPA와 μ—°λ™ν•œ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•˜λŠ” μ–΄λ…Έν…Œμ΄μ…˜, 이λ₯Ό 톡해 λ¦¬νŒŒμ§€ν„°λ¦¬μ™€ μ—”ν‹°ν‹° λ“±μ˜ 객체λ₯Ό ν…ŒμŠ€νŠΈ μ½”λ“œμ—μ„œ μ‚¬μš©ν•  수 있음

@DisplayName: ν…ŒμŠ€νŠΈ 이름을 뢙일 λ–„ μ‚¬μš©, κΈ°λ³Έ ν…ŒμŠ€νŠΈ 이름은 λ©”μ„œλ“œ 이름을 λ”°λΌκ°€μ§€λ§Œ λ©”μ„œλ“œ 이름은 κ·ΈλŒ€λ‘œ λ‘” 채 ν…ŒμŠ€νŠΈ 이름을 λ°”κΎΈκ³  μ‹Άμ„λ•Œ 이 μ–΄λ…Έν…Œμ΄μ…˜ μ‚¬μš©. @Display("ν…ŒμŠ€νŠΈ_결과에_보여쀄_이름)

@JoinColumn: ν•΄λ‹Ή 엔티티에 μ™Έλž˜ν‚€λ₯Ό μƒμ„±ν•˜λŠ” μ–΄λ…Έν…Œμ΄μ…˜, name μ†μ„±μœΌλ‘œ λ§€ν•‘ν•  μ™Έλž˜ν‚€ 이름 μ§€μ • @JoinColumn(name="μ™Έλž˜ν‚€_이름")

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published