Gearing up the MSA - TheOpenCloudEngine/uEngine-cloud GitHub Wiki

๊ธฐ๋ณธ์ ์ธ Micro Service๋ฅผ Springboot๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ•.

http://start.spring.io/ ์‚ฌ์ดํŠธ์— ๋“ค์–ด๊ฐ€์„œ ์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ๊ธฐ๋ณธ ํ”„๋กœ์ ํŠธ ํ…œํ”Œ๋ฆฟ์„ ์ƒ์„ฑํ•œ๋‹ค.

Group๊ณผ Artifact๋ฅผ ์ž…๋ ฅํ•œ๋‹ค. ํ•˜์ดํ”ˆ๊ณผ ์†Œ๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. Dependencies ์—

  • JPA - DB ORM
  • Rest Repository - ๊ธฐ๋ณธ Hateoas
  • H2 - ๊ธฐ๋ณธ ๋‚ด์žฅ DB

๋ฅผ ์ž…๋ ฅํ•˜๊ณ , Generate Project๋ฅผ ํด๋ฆญํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

Artifact ์— ์ž…๋ ฅํ•œ ํ”„๋กœ์ ํŠธ ๋ช…์œผ๋กœ zipํŒŒ์ผ์ด ๋‹ค์šด๋กœ๋“œ ๋œ๋‹ค.

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

๊ธฐ๋ณธ mavenํ”„๋กœ์ ํŠธ๋กœ ์ƒ์„ฑ๋œ ํด๋” ๊ตฌ์กฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์—ฌ๊ธฐ์„œ mvnw ํŒŒ์ผ์€ maven wrapper ๋กœ, ๋ฉ”์ด๋ธ์ด ์•„์˜ˆ ์—†๋Š” ์‚ฌ๋žŒ์ด ์žˆ๊ธฐ๋•Œ๋ฌธ์— ๋ฉ”์ด๋ธ ์„ค์น˜์—†์ด ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํŒŒ์ผ์ด๋‹ค. ํ•ด๋‹น ํ”„๋กœ์ ํŠธ๋กœ ํด๋”๋ฅผ ์ด๋™ํ•˜์—ฌ

mvn spring-boot:run ํ˜น์€ mvnw spring-boot:run

์„ ํ•˜๊ฒŒ๋˜๋ฉด ๋ฉ”์ด๋ธ์œผ๋กœ ๋ถ€ํ„ฐ library ๋ฅผ ๋‹ค์šด ๋ฐ›์€ ํ›„ ๊ธฐ๋ณธ ํฌํŠธ 8080์œผ๋กœ ์Šคํ”„๋ง ๋ถ€ํŠธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊ตฌ๋™ ๋œ๋‹ค.
์ž์‹ ์ด 8080ํฌํŠธ๋ฅผ ์‚ฌ์šฉ์ค‘์ด๊ณ , ๋‹ค๋ฅธ ํฌํŠธ๋กœ ๊ตฌ๋™์„ ์‹œํ‚ค๊ณ  ์‹ถ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์‹œ์ž‘ํ•˜๋ฉด ๋œ๋‹ค.

mvn spring-boot:run -Dserver.port=8081

[Tip Java9 ์ด์ƒ์—์„œ ๋นŒ๋“œ ์˜ค๋ฅ˜ - JAXB ๊ด€๋ จ Class ๋ชป์ฐพ๋Š” - ๊ฐ€ ๋‚˜๋Š” ๊ฒฝ์šฐ ์•„๋ž˜ dependency๋ฅผ pom.xml์— ์ถ”๊ฐ€ํ•œ๋‹ค]

   <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.0</version>
    </dependency>

ํ•ด๋‹น ์„œ๋น„์Šค ํ™•์ธํ•˜๊ธฐ

์ฝ˜์†”์ฐฝ์„ ํ•˜๋‚˜ ๋” ์—ฐ ํ›„์—, ํ•ด๋‹น ๊ฒฝ๋กœ๋กœ ์ด๋™์„ ํ•˜์—ฌ httpie ๋กœ ํ™•์ธ์„ ํ•ด๋ณผ์ˆ˜ ์žˆ๋‹ค. (Httpie-๋„๊ตฌ ์„ค์น˜ํ•˜๊ธฐ)

$ http localhost:8080

{
    "_links": {
        "profile": {
            "href": "http://localhost:8080/profile"
        }
    }
}

http localhost:8080 ๋ฅผ ์น˜๊ฒŒ๋˜๋ฉด ์œ„์— ์ฒ˜๋Ÿผ profile ์ •๋ณด๊ฐ€ ๋‚˜์˜ค๊ฒŒ๋˜๋Š”๋ฐ web์œผ๋กœ ์น˜๋ฉด ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ์ ‘๊ทผ์„ ํ•œ ๊ฒƒ์ด๋‹ค. ์–ผ๊ตด์ด ์—†๋‹ค ๋ฟ์ด์ง€, ๋ฐ์ดํ„ฐ๋งŒ ๋“ค์–ด๊ฐ€ ์žˆ๋‹ค.

๋””์ž์ธ ์š”์†Œ๋งŒ ๋นผ๊ณ , ๋ฐ์ดํ„ฐ๋งŒ ๋‚จ๊ธด๊ฒƒ์ด REST api ์ด๋‹ค.

์œ„์˜ ๋ฉ”์„ธ์ง€๋Š” profile์ด๋ผ๋Š” sub page๊ฐ€ ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค.

ํŒŒ์ผ ์ƒ์„ธ ์„ค๋ช…

src/SpringBootSampleApplication.java

@SpringBootApplication
public class SpringBootSampleApplication {
	public static void main(String[] args) {
		SpringApplication.run(SpringBootSampleApplication.class, args);
	}
}

main Class์ด๋‹ค.

**TIP: @SpringBootApplication **
์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์Šคํ”„๋ง๋ถ€ํŠธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค.
์š”์ฆ˜์—๋Š” ์ด๋Ÿฐ์‹์œผ๋กœ java class์•ˆ์—์„œ descriptive (๊ธฐ์ˆ ์ ์ธ), declarative(์„ ์–ธ์ ) ๋ฐฉ์‹์ด ์œ ํ–‰ํ•˜๊ณ ์žˆ๋‹ค. - ์˜ˆ์ „์—๋Š” xml ์—์„œ ์„ ์–ธํ•˜์˜€๋‹ค.

pom.xml

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.0.3.RELEASE</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
   ....
</dependencies>

parent ์—์„œ ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ธฐ๋ณธ์ ์ธ ์†์„ฑ๋“ค์„ ๊ฐ€์ ธ์˜จ๋‹ค.
์ตœ์ดˆ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ์‹œ ์„ค์ •ํ•˜์˜€๋˜ H2, JPA๋“ฑ์ด ์žˆ๋Š”๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.


์ด์ œ Person ์ด๋ผ๋Š” Entity ํด๋ ˆ์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ณด์ž

src/Person.java

@Entity
public class Person {
    @Id
    @GeneratedValue
    Long id;
    String name;
    int age;
    String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}

Id๋Š” ๋ฐ”๋€Œ๋ฉด ์•ˆ๋˜๋Š” ๊ฐ’์ด๊ธฐ ๋•Œ๋ฌธ์— id๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ๋ณ€์ˆ˜์— Get, Set ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

@Entity : ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค์ž„์„ ์ง€์ •ํ•˜๋ฉฐ ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋œ๋‹ค
@Id ๋งŒ ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ๋ณธ ํ‚ค๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ง์ ‘ ํ• ๋‹น ํ•˜๋Š” ์ „๋žต์ด๊ณ 
@GeneratedValue ์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์ž๋™ ์ƒ์„ฑ ์ „๋žต์ด๋‹ค.


REST ์„œ๋น„์Šค๋ฅผ ์—ด๊ธฐ ์œ„ํ•˜์—ฌ ์„œ๋น„์Šค ์œ ํ˜•์ค‘์— Repository ๋ผ๋Š” Interface๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ค€๋‹ค.
์ด๊ฒƒ์€ DDD(Domain Driven Design) ์—์„œ ํ•˜๋‚˜์˜ ํŒจํ„ด์ค‘ ํ•˜๋‚˜์ธ๋ฐ,
์–ด๋–ค Entity์— ์ ‘๊ทผํ•˜๋Š” crud๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ๊ธฐ๋ณธ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๋Š” ์„œ๋น„์Šค์˜ ์œ ํ˜•์„ Repository ์ด๋ฆ„์„ ๋ถ™์—ฌ์„œ ์‚ฌ์šฉํ•œ๋‹ค.

src/PersonRepository.java

public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
}

PagingAndSortingRepository ๋Š” ํŽ˜์ด์ง• ๊ธฐ๋Šฅ์„ ๋‹ด๊ณ  ์žˆ๋Š” Repository์ด๋‹ค
๋ ˆํŒŒ์ง€ํ† ๋ฆฌ์˜ ์ƒˆ๋กœ์šด ์œ ํ˜•์„ ๋งŒ๋“ค๊ณ  ์‹ถ์„๋•Œ๋Š” ํ•ด๋‹น Repository๋ฅผ extendsํ•ด์„œ ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋Š” ์—†์ง€๋งŒ Repository ์ƒ์„ฑ์‹œ ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ runtime์— ์‹ค์ œ ์›Œํ‚นํ•˜๋Š” sql๋ฌธ์„ ์ƒ์„ฑํ•˜๊ณ  ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฌ๋Š” ๊ฒƒ์„ ๋งŒ๋“ค์–ด์ค€๋‹ค


์ด์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋‹ค์‹œ run์„ ํ•œ ํ›„์— ์ฝ˜์†”์ฐฝ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด profile์„ ์กฐํšŒํ•˜์—ฌ ๋ณด์ž (httpie ๋„๊ตฌ ์„ค์น˜ํ•˜๊ธฐ)

$ http http://localhost:8080/

{
    "_links": {
        "persons": {
            "href": "http://localhost:8080/persons{?page,size,sort}",
            "templated": true
        },
        "profile": {
            "href": "http://localhost:8080/profile"
        }
    }
}

Person์ด๋ผ๋Š” ํด๋ž˜์Šค์™€ PersonRepository๋ฅผ ์ƒ์„ฑํ•˜์˜€๋”๋‹ˆ
http://localhost:8080/profile/persons{?page,size,sort} ์ด๋ผ๋Š” link๊ฐ€ ์ƒ๊ธด ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

$ http http://localhost:8080/persons page==1

์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜์˜€์„๋•Œ ๋‚˜์˜ค๋Š” ๊ฐ’์ด ์—†๋Š”๊ฒƒ์„ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค. ์กฐํšŒ์‹œ ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์€ == ๊ฐ€ ๋‘๊ฒŒ๋ฅผ ์จ์•ผ ํ•œ๋‹ค.


์ด์ œ POST๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด๋ณด์ž

$ http POST http://localhost:8080/persons name="ํ™๊ธธ๋™" address="์„œ์šธ์‹œ ๊ฐ•๋‚จ๊ตฌ ๋…ผํ˜„๋™ 111"

{
    "_links": {
        "person": {
            "href": "http://localhost:8080/persons/1"
        },
        "self": {
            "href": "http://localhost:8080/persons/1"
        }
    },
    "address": "ํ™๊ธธ๋™",
    "age": 0,
    "name": "์„œ์šธ์‹œ ๊ฐ•๋‚จ๊ตฌ ๋…ผํ˜„๋™ 111"
}

๋„ฃ์–ด์ง„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝ ํ• ์ ์—๋Š” PATCH๋ฅผ ์“ฐ๋ฉด ๋œ๋‹ค

$ http PATCH "http://localhost:8080/persons/1" age=10
{
    "_links": {
        "person": {
            "href": "http://localhost:8080/persons/1"
        },
        "self": {
            "href": "http://localhost:8080/persons/1"
        }
    },
    "address": "ํ™๊ธธ๋™",
    "age": 10,
    "name": "์„œ์šธ์‹œ ๊ฐ•๋‚จ๊ตฌ ๋…ผํ˜„๋™ 111"
}

๋ฐ์ดํ„ฐ๋ฅผ ๋งŽ์ด ๋„ฃ์€ ํ›„ page์™€ sort ๋„ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

$ http "http://localhost:8080/persons/1" page==1 size==5
$ http "http://localhost:8080/persons/1" page==1 size==5 sort=="name"
$ http "http://localhost:8080/persons/1" page==1 size==5 sort=="name,asc"

์ด๋ ‡๊ฒŒ ๊ธฐ๋ณธ์ ์ธ ์ˆ˜์ค€๋†’์€ (HATEOAS) RESTful API ๋ฅผ ์ž๋™์ƒ์„ฑํ•จ.

์ฐธ๊ณ : https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9

โš ๏ธ **GitHub.com Fallback** โš ๏ธ