๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring

[Spring] ์Šคํ”„๋ง ์˜ˆ์ œ PetClinic ํ”„๋กœ์ ํŠธ ๋ถ„์„ ๋ฐ ๊ธฐ๋Šฅ ๋ณ€๊ฒฝ

by Leica 2020. 2. 28.
๋ฐ˜์‘ํ˜•

[Spring] ์Šคํ”„๋ง ์˜ˆ์ œ PetClinic ํ”„๋กœ์ ํŠธ ๋ถ„์„ ๋ฐ ๊ธฐ๋Šฅ ๋ณ€๊ฒฝ

1. ํ”„๋กœ์ ํŠธ ๋กœ๊ทธ ๋ ˆ๋ฒจ ๋ณ€๊ฒฝ

๋กœ๊ทธ๋ฅผ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ์˜ ์‹คํ–‰ ํ๋ฆ„์„ ์‚ดํŽด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

PetClinic ํ”„๋กœ์ ํŠธ๋Š” ๊ธฐ๋ณธ ๋กœ๊ทธ ๋ ˆ๋ฒจ์ด INFO๋กœ ๋˜์–ด์žˆ์–ด์„œ ์ž์„ธํ•œ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์—†๋‹ค.

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ด๊ฒƒ์ €๊ฒƒ ๋ˆŒ๋Ÿฌ๋ด๋„ ๋”ฑํžˆ ์ถœ๋ ฅ๋˜๋Š” ๋กœ๊ทธ๊ฐ€ ์—†๋‹ค.

๋กœ๊ทธ ๋ ˆ๋ฒจ์„ DEBUG๋กœ ๋ณ€๊ฒฝํ•˜์ž.

Spring boot ํ”„๋กœ์ ํŠธ๋Š” src/main/resources/application.properties ํŒŒ์ผ์— ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ์„ค์ •ํ•œ๋‹ค.

 

application.properties

1
2
3
# Logging
#logging.level.org.springframework=INFO
logging.level.org.springframework.web=DEBUG
cs

 

๊ธฐ์กด์—๋Š” logging.level.org.springframework=INFO๋กœ ์„ค์ •๋ผ์žˆ๊ณ  ๊ทธ ์•„๋ž˜ logging.level.org.springframework.web=DEBUG๋Š” ์ฃผ์„์ฒ˜๋ฆฌ๊ฐ€ ๋˜์–ด์žˆ๋‹ค.

๋กœ๊ทธ ๋ ˆ๋ฒจ์„ DEBUG๋กœ ๋ฐ”๊พธ๊ธฐ ์œ„ํ•ด ์œ„์™€ ๊ฐ™์ด ...INFO๋ฅผ ์ฃผ์„์ฒ˜๋ฆฌํ•˜๊ณ  ...DEBUG์˜ ์ฃผ์„์ฒ˜๋ฆฌ๋ฅผ ํ’€์–ด์ค€๋‹ค.

๊ทธ๋ฆฌ๊ณ  PetClinic์„ ์žฌ์‹คํ–‰ํ•˜๋ฉด,

 

์ด์ œ ์ž์„ธํ•œ DEBUG ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋˜๊ธฐ ์‹œ์ž‘ํ•œ๋‹ค.

 

2. ํ”„๋กœ์ ํŠธ ํ๋ฆ„ ๋ถ„์„

ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด DispatcherServlet์ด ์ดˆ๊ธฐํ™”๋œ๋‹ค.

 

FIND OWNERS๋ฅผ ํด๋ฆญํ•ด๋ณด์ž.

์š”์ฒญ URL์ด /owners/find๋กœ ์ „์†ก๋œ๋‹ค.

 

๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด DispatcherServlet์ด ํ•ด๋‹น GET "/owners/find" ์š”์ฒญ์„ ๋ฐ›์•„ ๋‹ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ์ธ OwnerController๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

 

OwnerController์˜ initFindForm()์— @GetMapping ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ "/owners/find"์ด ์ง€์ •๋˜์–ด ์žˆ๋‹ค.

"/owners/find" URL๋กœ GET ์š”์ฒญ์ด ์˜ค๋ฉด ์ด ๋ฉ”์†Œ๋“œ์™€ ๋งคํ•‘ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

initFindForm()์€ owners/findOwners ๋ทฐ๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

 

์ด ๋ทฐ ํŒŒ์ผ์€ ํ”„๋กœ์ ํŠธ resources/templates/owners/findOwners.html์— ์žˆ๋‹ค.

 

findOwners ๋ทฐ๊ฐ€ ์ด ํ™”๋ฉด์— ํ•ด๋‹นํ•œ๋‹ค.

๋‹ค๋ฅธ ๊ธฐ๋Šฅ๋„ ์ด์™€ ๊ฐ™์ด ๋ถ„์„ํ•˜๋ฉด ๋œ๋‹ค.

 

3. ๊ธฐ๋Šฅ ๋ณ€๊ฒฝํ•˜๊ธฐ

1) FIND OWNERS ๋ฉ”๋‰ด์—์„œ Last name์„ First name์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ

PetClinic ํ”„๋กœ์ ํŠธ์—๋Š” ์ด๋ฏธ owner๋“ค์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ฑ๋ก๋˜์–ด ์žˆ๋‹ค.

 

๊ฒ€์ƒ‰์„ ์š”์ฒญํ•ด์„œ ์š”์ฒญ URL๊ณผ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด๋ณด์ž.

Jean Coleman์ด๋ผ๋Š” ์ด๋ฆ„์˜ owner๊ฐ€ ์กด์žฌํ•˜์ง€๋งŒ ํ˜„์žฌ๋Š” Last name์„ ๊ฒ€์ƒ‰ํ•˜๋ฏ€๋กœ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š๋Š”๋‹ค.

 

๋กœ๊ทธ๋ฅผ ํ†ตํ•ด owner ๊ฒ€์ƒ‰ ์š”์ฒญ ์‹œ OwnerController์˜ processFindForm()์ด ์ฒ˜๋ฆฌํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์˜ ๋‚ด์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@GetMapping("/owners")
public String processFindForm(Owner owner, BindingResult result, Map<String, Object> model) {
 
    // allow parameterless GET request for /owners to return all records
    if (owner.getLastName() == null) {
        owner.setLastName(""); // empty string signifies broadest possible search
    }
 
    // find owners by last name
    Collection<Owner> results = this.owners.findByLastName(owner.getLastName());
    if (results.isEmpty()) {
        // no owners found
        result.rejectValue("lastName""notFound""not found");
        return "owners/findOwners";
    }
    else if (results.size() == 1) {
        // 1 owner found
        owner = results.iterator().next();
        return "redirect:/owners/" + owner.getId();
    }
    else {
        // multiple owners found
        model.put("selections", results);
        return "owners/ownersList";
    }
}
cs

 

firstName์— ๋Œ€ํ•ด ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝํ•ด์ค€๋‹ค.

Owner ํด๋ž˜์Šค์— firstName์˜ getter์™€ setter๋Š” ์ด๋ฏธ ์žˆ์œผ๋ฏ€๋กœ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•„๋„ ๋˜์ง€๋งŒ

findByFirstName() ๋ฉ”์†Œ๋“œ๋Š” ์—†์–ด์„œ ์ถ”๊ฐ€๋กœ ์ •์˜ํ•ด์•ผํ•œ๋‹ค.

 

findByLastName()์ด ์ •์˜๋˜์–ด์žˆ๋Š” OwnerRepository ์ธํ„ฐํŽ˜์ด์Šค์— ์•„๋ž˜์™€ ๊ฐ™์ด findByFirstName()์„ ์ƒˆ๋กœ ์ •์˜ํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ @Param์œผ๋กœ ์ •์˜ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ firstName์„ ์œ„์˜ @Query์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ ๋ทฐ์—์„œ ๊ธฐ์กด ํŒŒ๋ผ๋ฏธํ„ฐ๋ช… lastName์„ firstName์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.

 

์ด์ œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ์‹คํ–‰ํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด๋ณด์ž.

 

First name์œผ๋กœ owner๋ฅผ ๊ฒ€์ƒ‰ํ•ด๋ณด์ž.

 

First name์œผ๋กœ ๊ฒ€์ƒ‰ํ•œ ๊ฒฐ๊ณผ

 

2) ๋ถ€๋ถ„ ์ผ์น˜ ๊ฒ€์ƒ‰์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ

ํ˜„์žฌ๋Š” ์ฟผ๋ฆฌ๊ฐ€ LIKE :firstName%๊ณผ ๊ฐ™์ด ๋˜์–ด์žˆ์–ด ๋ฌธ์ž์—ด์˜ ์•ž๋ถ€๋ถ„์€ ์™„์ „ํžˆ ์ผ์น˜ํ•ด์•ผ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ๋‹ค.

 

๊ฐ„๋‹จํžˆ ์ฟผ๋ฆฌ๋ฅผ ๋ณ€๊ฒฝํ•ด์„œ ํ•ด๋‹น ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

Owner ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๋Š” OwnerRepository ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ด๋™ํ•œ๋‹ค.

 

findByFirstName() ๋ฉ”์†Œ๋“œ์˜ ์ฟผ๋ฆฌ๋ฅผ ์œ„์™€ ๊ฐ™์ด ๋ณ€๊ฒฝํ•œ๋‹ค.

 

์ด์ œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ์‹คํ–‰ํ•˜๊ณ  ๋‹ค์‹œ first name์˜ ์ผ๋ถ€ ๋ฌธ์ž์—ด๋กœ ๊ฒ€์ƒ‰์„ ์š”์ฒญํ•ด๋ณด์ž.

 

'ea'์œผ๋กœ ๊ฒ€์ƒ‰ํ•œ ๊ฒฐ๊ณผ

 

3) Add Owner ๊ธฐ๋Šฅ์— Age ํ•„๋“œ ์ถ”๊ฐ€ํ•˜๊ธฐ

ํ˜„์žฌ add owner์˜ ํ•„๋“œ๋Š” ์œ„์™€ ๊ฐ™๋‹ค.

์—ฌ๊ธฐ์— Age๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

 

๋จผ์ € ๋ทฐ์˜ form์— Age๋ฅผ ์ž…๋ ฅํ•˜๋Š” input ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

 

Owner ํด๋ž˜์Šค์— ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜ age์™€ getter, setter๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

 

resources/db/hsqldb/schema.sql

1
2
3
4
5
6
7
8
9
CREATE TABLE owners (
  id         INTEGER IDENTITY PRIMARY KEY,
  first_name VARCHAR(30),
  last_name  VARCHAR_IGNORECASE(30),
 age        INTEGER NOT NULL,
  address    VARCHAR(255),
  city       VARCHAR(80),
  telephone  VARCHAR(20)
);
cs

owners ํ…Œ์ด๋ธ”์— age ์ปฌ๋Ÿผ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

 

์ฐธ๊ณ ๋กœ ์–ด๋–ค ์Šคํ‚ค๋งˆ ํŒŒ์ผ์„ ์ฐธ์กฐํ•˜๋Š”์ง€๋Š” resources/application.properties ํŒŒ์ผ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

resources/db/hsqldb/data.sql

1
2
3
4
5
6
7
8
9
10
INSERT INTO owners(id, first_name, last_name, age, address, city, telephone) VALUES (1'George''Franklin'30'110 W. Liberty St.''Madison''6085551023');
INSERT INTO owners(id, first_name, last_name, age, address, city, telephone) VALUES (2'Betty''Davis'41'638 Cardinal Ave.''Sun Prairie''6085551749');
INSERT INTO owners(id, first_name, last_name, age, address, city, telephone) VALUES (3'Eduardo''Rodriquez'21'2693 Commerce St.''McFarland''6085558763');
INSERT INTO owners(id, first_name, last_name, age, address, city, telephone) VALUES (4'Harold''Davis'32'563 Friendly St.''Windsor''6085553198');
INSERT INTO owners(id, first_name, last_name, age, address, city, telephone) VALUES (5'Peter''McTavish'55'2387 S. Fair Way''Madison''6085552765');
INSERT INTO owners(id, first_name, last_name, age, address, city, telephone) VALUES (6'Jean''Coleman'74'105 N. Lake St.''Monona''6085552654');
INSERT INTO owners(id, first_name, last_name, age, address, city, telephone) VALUES (7'Jeff''Black'19'1450 Oak Blvd.''Monona''6085555387');
INSERT INTO owners(id, first_name, last_name, age, address, city, telephone) VALUES (8'Maria''Escobito'29'345 Maple St.''Madison''6085557683');
INSERT INTO owners(id, first_name, last_name, age, address, city, telephone) VALUES (9'David''Schroeder'67'2749 Blackhawk Trail''Madison''6085559435');
INSERT INTO owners(id, first_name, last_name, age, address, city, telephone) VALUES (10'Carlos''Estaban'40'2335 Independence La.''Waunakee''6085555487');
cs

ํ…Œ์ด๋ธ” ์Šคํ‚ค๋งˆ๋ฅผ ๋ณ€๊ฒฝํ–ˆ์œผ๋ฏ€๋กœ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘๋ ๋•Œ ์‹คํ–‰๋˜๋Š” INSERT๋ฌธ์„ ๋ณ€๊ฒฝํ•œ๋‹ค.

 

์ด์ œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋‚˜ owner ๋ชฉ๋ก์—์„œ age๊ฐ€ ์ถ”๊ฐ€๋œ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ด€๋ จ ๋ทฐ๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.

 

resources/templates/owners/ownerDetails.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<table class="table table-striped" th:object="${owner}">
  <tr>
    <th>Name</th>
    <td><b th:text="*{firstName + ' ' + lastName}"></b></td>
  </tr>
  <tr>
    <th>Age</th>
    <td th:text="*{age}" /></td>
  </tr>
  <tr>
    <th>Address</th>
    <td th:text="*{address}" /></td>
  </tr>
  <tr>
    <th>City</th>
    <td th:text="*{city}" /></td>
  </tr>
  <tr>
    <th>Telephone</th>
    <td th:text="*{telephone}" /></td>
  </tr>
</table>
cs

 

resources/templates/owners/ownerList.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<table id="owners" class="table table-striped">
    <thead>
    <tr>
        <th style="width: 150px;">Name</th>
        <th style="width: 100px;">age</th>
        <th style="width: 200px;">Address</th>
        <th>City</th>
        <th style="width: 120px">Telephone</th>
        <th>Pets</th>
    </tr>
    </thead>
    <tbody>
      <tr th:each="owner : ${selections}">
          <td>
              <a th:href="@{/owners/__${owner.id}__}" th:text="${owner.firstName + ' ' + owner.lastName}"/></a>
          </td>
          <td th:text="${owner.age}"/>
          <td th:text="${owner.address}"/>
          <td th:text="${owner.city}"/>
          <td th:text="${owner.telephone}"/>
          <td><span th:each="pet : ${owner.pets}" th:text="${pet.name} "/></td>
      </tr>
    </tbody>
</table>
cs

 

์ด์ œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ์‹œ์ž‘ํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด๋ณด์ž.

 

๐Ÿ’ก ๋ทฐ(html)๋งŒ ๋ณ€๊ฒฝํ•œ ๊ฒฝ์šฐ์—๋Š” ์žฌ์‹œ์ž‘ํ•˜์ง€์•Š๊ณ  build๋งŒ ํ•ด๋„ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์ ์šฉ๋œ๋‹ค.

 

์‹ ๊ทœ owner ๋“ฑ๋ก ํผ์— Age๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.

 

์‹ ๊ทœ owner ๋“ฑ๋ก ๊ฒฐ๊ณผ์— Age๊ฐ€ ํ•จ๊ป˜ ์ถœ๋ ฅ๋œ๋‹ค.

 

Owner list์— age ํ•„๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.

 

References

์ธํ”„๋Ÿฐ - ๋ฐฑ๊ธฐ์„ ๋‹˜์˜ ์˜ˆ์ œ๋กœ ๋ฐฐ์šฐ๋Š” ์Šคํ”„๋ง ์ž…๋ฌธ(๊ฐœ์ •ํŒ)์„ ์ˆ˜๊ฐ•ํ•˜๋ฉฐ ์ •๋ฆฌํ•œ ํฌ์ŠคํŒ…์ž…๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€