chap 13 - JAVA-JIKIMI/SPRING-IN-ACTION-5 GitHub Wiki

PART 4. ํด๋ผ์šฐ๋“œ ๋„ค์ดํ‹ฐ๋ธŒ ์Šคํ”„๋ง

์Šคํ”„๋ง ํด๋ผ์šฐ๋“œ, ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ฐœ๋ฐœ์„ ์†Œ๊ฐœํ•˜๋ฉด์„œ ๋‹จ์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ MSA๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณธ๋‹ค. 13์žฅ์—์„œ๋Š” MSA์˜ ๊ฐœ์š”๋ฅผ ์•Œ์•„๋ณด๊ณ  ์„œ๋น„์Šค ๋ฐœ๊ฒฌ(discovery)๋ฅผ ์•Œ์•„๋ณผ ๊ฒƒ์ด๋‹ค. ์ด ๋•Œ, ์Šคํ”„๋ง ๊ธฐ๋ฐ˜์˜ MSA๋ฅผ ๋“ฑ๋กํ•˜๊ณ  ๋ฐœ๊ฒฌํ•˜๊ธฐ ์œ„ํ•ด ๋„ทํ”Œ๋ฆญ์Šค์˜ ์œ ๋ ˆ์นด ์„œ๋น„์Šค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

13์žฅ. ์„œ๋น„์Šค ํƒ๊ตฌํ•˜๊ธฐ

MSA ์•Œ์•„๋ณด๊ธฐ
์„œ๋น„์Šค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ƒ์„ฑํ•˜๊ธฐ
์„œ๋น„์Šค ๋“ฑ๋ก ๋ฐ ๋ฐœ๊ฒฌํ•˜๊ธฐ

MSA๋ž€?

  • ๋‹ˆ๋ชจ์˜ ๋ฌธํ”ผ์‰ฌ์™€ ๊ฐ™๋‹ค.
  • ๊ฐ๊ฐ์˜ ๋ฌธํ”ผ์‰ฌ๋“ค์ด ๋ชจ์—ฌ ํ™ฉ์ƒˆ์น˜, ๋ฌธ์„œ, ๋ฐฐ ๋“ฑ ์žฌ๋ฏธ์žˆ๋Š” ๋ชจ์–‘์„ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค.
  • ๊ทธ๋Ÿฌ๋‚˜ ๊ฐ๊ฐ์˜ ๋ฌธํ”ผ์‰ฌ๋Š” ์ž์‹ ๋งŒ์˜ ๋น„๋Š˜, ์ง€๋А๋Ÿฌ๋ฏธ, ์•„๊ฐ€๋ฏธ, ๋ˆˆ, ์žฅ๊ธฐ, ๊ทธ๋ฆฌ๊ณ  ํฌ๋ง๊ณผ ๊ฟˆ์„ ๊ฐ€์ง„๋‹ค.

https://vignette.wikia.nocookie.net/disney/images/1/16/Moonfish-ship.jpg/revision/latest?cb=20150611172306

1. MSA ์ดํ•ดํ•˜๊ธฐ

Monolithic Architecture

์„œ๋น„์Šค์˜ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ ๋ชจ๋“  ์„œ๋น„์Šค๋ฅผ ํ•˜๋‚˜์˜ ํŒจํ‚ค์ง€์— ๋‹ด์•„ ๋นŒ๋“œํ•˜๊ณ  ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ๋ฒ•

๋‹จ์ผ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋‹จ์ 

  • ์ „์ฒด๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต๋‹ค
  • ํ…Œ์ŠคํŠธ๊ฐ€ ๋” ์–ด๋ ต๋‹ค
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐ„์˜ ์ถฉ๋Œ์ด ์ƒ๊ธฐ๊ธฐ ์‰ฝ๋‹ค
  • ํ™•์žฅ ์‹œ์— ๋น„ํšจ์œจ์ ์ด๋‹ค
  • ์ ์šฉํ•  ํ…Œํฌ๋†€๋Ÿฌ์ง€๋ฅผ ๊ฒฐ์ •ํ•  ๋•Œ๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค(์–ธ์–ด, ํ”„๋ ˆ์ž„์›Œํฌ..)
  • ํ”„๋กœ๋•์…˜์œผ๋กœ ์ด์–‘ํ•˜๊ธฐ ์œ„ํ•ด ๋งŽ์€ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•˜๋‹ค
  • ๋ถ€๋ถ„์˜ ์žฅ์• ๊ฐ€ ์ „์ฒด ์„œ๋น„์Šค์˜ ์žฅ์• ๋กœ ์ด์–ด์ง„๋‹ค.(out of memory...)

๋‹จ์ผ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋Œ€์กฐ๋˜๋Š” ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ณ์˜ ์žฅ์  - ๋ณต์žก์„ฑ ํ•ด๊ฒฐ

  • ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ๊ฐ€ ์‰ฝ๋‹ค
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋น„ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ์ง€ ์•Š๋Š”๋‹ค(๋œํ•˜๋‹ค)
  • ๋…์ž์ ์œผ๋กœ ๊ทœ๋ชจ๋ฅผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค
  • ๊ฐ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์— ์ ์šฉํ•  ํ…Œํฌ๋†€๋Ÿฌ์ง€๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค
  • ์–ธ์ œ๋“  ํ”„๋กœ๋•์…˜์œผ๋กœ ์ด์–‘ํ•  ์ˆ˜ ์žˆ๋‹ค

์ฐธ๊ณ 
Nginx ๊ณต์‹๋ฌธ์„œ (https://www.nginx.com/blog/introduction-to-microservices/)

2. ์„œ๋น„์Šค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์„ค์ •ํ•˜๊ธฐ

์œ ๋ ˆ์นด

๋ญ”๊ฐ€๋ฅผ ๋ฐœ๊ฒฌํ•˜๊ฑฐ๋‚˜ ์•Œ์•„๋ƒˆ์„ ๋•Œ ์ง€๋ฅด๋Š” ๊ธฐ์จ์˜ ํƒ„์„ฑ.

๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์—์„œ ์„œ๋กœ๋ฅผ ์ฐพ์„ ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ์„œ๋น„์Šค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์˜ ์ด๋ฆ„

์œ ๋ ˆ์นด๋Š” ๋ชจ๋“  ์„œ๋น„์Šค์˜ ์ค‘์•™ ์ง‘์ค‘ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋กœ ์ž‘๋™ํ•œ๋‹ค.

์œ ๋ ˆ์นด ์ž์ฒด๋„ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

https://postfiles.pstatic.net/MjAyMDEwMTdfOCAg/MDAxNjAyODk1OTk0MDMw.-XlXtl1pcKFX7EjhkPKDA4GMbaC4laxR1DAbW7NtCdEg.2WKv3offucuiJ7yg5BZWF-0qu2T_mvIV71khgTBGNZkg.PNG.willow_weed/EureKa.png?type=w773

๋ชจ๋“  ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋Š” ์œ ๋ ˆ์นด์— ์ž์‹ ์„ 30์ดˆ์— ํ•œ ๋ฒˆ์”ฉ ๋“ฑ๋กํ•œ๋‹ค

other service๊ฐ€ ์œ ๋ ˆ์นด์—์„œ 'some service'๋ž€ ์ด๋ฆ„์œผ๋กœ ์ฐพ๋Š”๋‹ค.

some service๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ๋ผ๋ฉด ๋ฆฌ๋ณธ์ด ์ธ์Šคํ„ด์Šค๋ฅผ ์„ ํƒํ•˜๊ณ  ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

๋ฆฌ๋ณธ

ํด๋ผ์ด์–ธํŠธ ์ธก์˜ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ

์ค‘์•™์ง‘์ค‘ํ™”๋œ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ์™€ ๋น„๊ตํ•ด ์žฅ์ ์„ ๊ฐ€์ง„๋‹ค.

  1. ํด๋ผ์ด์–ธํŠธ ์ˆ˜์— ๋น„๋ก€ํ•ด ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ํฌ๊ธฐ๊ฐ€ ์กฐ์ •๋œ๋‹ค.
  2. ๊ฐ ํด๋ผ์ด์–ธํŠธ์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

์œ ๋ ˆ์นด ํ”„๋กœ์ ํŠธ1 ์ƒ์„ฑํ•˜๊ธฐ

ํ”„๋กœ์ ํŠธ๋ช… service-registry

ํŒจํ‚ค์ง€ ์ด๋ฆ„ taco

์Šคํ”„๋ง ์Šคํƒ€ํ„ฐ ํ”„๋กœ์ ํŠธ ์˜์กด์„ฑ ๋Œ€ํ™”์ƒ์ž์— Spring Cloud Discovery๋ฅผ ํ™•์žฅ, Eureka Server๋ฅผ ์„ ํƒํ•˜๊ณ  Finish๋ฅผ ํด๋ฆญ.

1. pom.xml ํ™•์ธ

<properties>
...
		<!-- ๋‹ค๋ฅธ ๋ฒ„์ „์˜ ์Šคํ”„๋ง ํด๋ผ์šฐ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ ๋ณ€๊ฒฝ -->
		<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
</properties>
...
<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>
...
</dependencies>
...
<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
</dependencyManagement>

2. ServiceRegistryApplication ์ˆ˜์ •

...
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootAppliation
@EnableEurekaServer // ์ถ”๊ฐ€!
public class ServiceRegistryApplication {

	@Test
	public void contextLoads() {
	}

}

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘๋˜๋Š” ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ํด๋ž˜์Šค์ธ ServiceRegistryApplication์„ ์ˆ˜์ •ํ•œ๋‹ค.

์œ ๋ ˆ์นด ์„œ๋ฒ„๊ฐ€ ํ™œ์„ฑํ™”๋œ๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€ ์ž‘์—…ํ•˜๊ณ  ์•ฑ์„ ์‹œ์ž‘ํ•˜๋ฉด ์›น๋ธŒ๋ผ์šฐ์ €์—์„œ ์œ ๋ ˆ์นด ๋Œ€์‹œ๋ณด๋“œ๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค.

๋Œ€์‹œ๋ณด๋“œ์—์„œ ๊ทธ ์ค‘ ์–ด๋–ค ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค๊ฐ€ ์œ ๋ ˆ์นด์— ๋“ฑ๋ก๋˜์—ˆ๋Š”์ง€๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์„œ๋น„์Šค๋ฅผ ๋“ฑ๋กํ•  ๋•Œ ์œ ๋ ˆ์นด ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ์ž์ฃผ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

์œ ๋ ˆ์นด๊ฐ€ 30์ดˆ์— ํ•œ๋ฒˆ์”ฉ ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์ค‘์ธ๋ฐ, ์•„์ง ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋ฅผ ์™„์ „ํ•˜๊ฒŒ ๊ตฌ์„ฑํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด๋ฉฐ, ์œ ๋ ˆ์นด๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์€ ๋งž๋‹ค.

์žฅ์• ์— ๋Œ€๋น„ํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์œ ๋ ˆ์นด ์„œ๋ฒ„๊ฐ€ ํ•จ๊ป˜ ๋™์ž‘ํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•˜๋‹ค.

๋”ฐ๋ผ์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์œ ๋ ˆ์นด๋Š” ๋‹ค๋ฅธ ์œ ๋ ˆ์นด ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์„œ๋น„์Šค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ์œ ๋ ˆ์นด ์„œ๋ฒ„์˜ ์„œ๋น„์Šค๋กœ ์ž์‹ ์„ ๋“ฑ๋กํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

๋•Œ๋ฌธ์— ํ”„๋กœ๋•์…˜ ์„ค์ •์—์„œ๋Š” ์œ ๋ ˆ์นด๋ฅผ ๋‘ ๋Œ€ ์ด์ƒ ๋™์ž‘ํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฐœ๋ฐœ ๋ชฉ์ ์œผ๋กœ๋Š” ํ•˜๋‚˜์˜ ์œ ๋ ˆ์นด ์„œ๋ฒ„๋ฉด ์ถฉ๋ถ„ํ•˜๋‹ค.

์ด ๋•Œ ์œ ๋ ˆ์นด ์„œ๋ฒ„๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌ์„ฑํ•˜์ง€ ์•Š์œผ๋ฉด 30์ดˆ๋งˆ๋‹ค ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. ์œ ๋ ˆ์นด๋Š” 30์ดˆ๋งˆ๋‹ค ๋‹ค๋ฅธ ์œ ๋ ˆ์นด ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•˜๋ฉด์„œ ์ž์‹ ์ด ์ž‘๋™ ์ค‘์ž„์„ ์•Œ๋ฆฌ๊ณ  ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ •๋ณด๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์œ ๋ ˆ์นด ์„œ๋ฒ„๊ฐ€ ํ˜ผ์ž์ž„์„ ์•Œ๋„๋ก ๊ตฌ์„ฑํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

3. application.yml ์ถ”๊ฐ€

๋‹จ์ผ ์œ ๋ ˆ์นด ์„œ๋ฒ„ ๊ตฌ์„ฑ

server:
  port: 8761
  
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

eureka.client.registerWithEureka, eureka.client.fetchRegistry๋Š” ์œ ๋ ˆ์นด๊ฐ€ ๋‹ค๋ฅธ ์œ ๋ ˆ์นด์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ๋Š” ์†์„ฑ์ด๋‹ค. ๋‘ ์†์„ฑ์˜ ๊ธฐ๋ณธ๊ฐ’์„ true๋กœ, ํ•ด๋‹น ์œ ๋ ˆ์นด ์„œ๋ฒ„๊ฐ€ ๋‹ค๋ฅธ ์œ ๋ ˆ์นด ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๊ฒŒ ํ•  ๊ฒƒ์ธ์ง€(fetchRegistry), ๋‹ค๋ฅธ ์œ ๋ ˆ์นด ์„œ๋ฒ„์˜ ์„œ๋น„์Šค๋กœ ์ž์‹ ์„ ๋“ฑ๋กํ•  ๊ฒƒ์ธ์ง€(registerWithEureka)๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

serviceUrl.defaultZone์€ ์œ ๋ ˆ์นด url์„ ๋งํ•œ๋‹ค. ์ค‘๊ด„ํ˜ธ ์•ˆ์— ์ง€์ •๋œ ๋‹ค๋ฅธ ์†์„ฑ๊ฐ’์œผ๋กœ ๋Œ€์ฒด๋˜์–ด ์‹ค ์ฃผ์†Œ๋Š” http://localhost:8761/eureka ๊ฐ€ ๋œ๋‹ค.

์ž์ฒด ๋ณด์กด ๋ชจ๋“œ ๋น„ํ™œ์„ฑํ™”

server:
    enable-self-preservation: false

์œ ๋ ˆ์นด ์„œ๋ฒ„๋Š”์ธ์Šคํ„ด์Šค๊ฐ€ ์ž์‹ ์„ ๋“ฑ๋กํ•˜๊ณ  ๋“ฑ๋ก ๊ฐฑ์‹  ์š”์ฒญ์„ 30์ดˆ๋งˆ๋‹ค ์ „์†กํ•˜๊ธฐ๋ฅผ ๊ธฐ๋Œ€ํ•œ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์„ธ ๋ฒˆ์˜ ๊ฐฑ์‹ ๊ธฐ๊ฐ„(90์ดˆ) ๋™์•ˆ ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค๋กœ๋ถ€ํ„ฐ ๋“ฑ๋ก ๊ฐฑ์‹  ์š”์ฒญ์„ ๋ฐ›์ง€ ๋ชปํ•˜๋ฉด ํ•ด๋‹น ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค์˜ ๋“ฑ๋ก์„ ์ทจ์†Œํ•˜๊ฒŒ ๋œ๋‹ค. ๋งŒ์ผ ์ด๋ ‡๊ฒŒ ์ค‘๋‹จ๋˜๋Š” ์„œ๋น„์Šค๊ฐ€ ์ž„๊ณ„๊ฐ’(threshold)์„ ์ดˆ๊ณผํ•˜๋ฉด ์œ ๋ ˆ์นด ์„œ๋ฒ„๋Š” ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•˜๊ณ  ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋“ฑ๋ก๋œ ๋‚˜๋จธ์ง€ ์„œ๋น„์Šค ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์กดํ•˜๊ธฐ ์œ„ํ•ด ์ž์ฒด ๋ณด์กด(self-preservation) ๋ชจ๋“œ๊ฐ€ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์ถ”๊ฐ€์ ์ธ ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค์˜ ๋“ฑ๋ก ์ทจ์†Œ๊ฐ€ ๋ฐฉ์ง€๋œ๋‹ค.

ํ”„๋กœ๋•์…˜ ์„ค์ •์—์„œ๋Š” ์ด ๊ฐ’์„ true๋กœ ์„ค์ •ํ•ด ์‹ค์ œ ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ ๋‚˜๋จธ์ง€ ํ™œ์„ฑํ™”๋œ ์„œ๋น„์Šค๋“ค์˜ ๋“ฑ๋ก ์ทจ์†Œ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฐœ๋ฐœ ์„œ๋ฒ„์—์„œ๋Š” ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ด ๊ฐ’์„ false๋กœ ํ•œ๋‹ค. ์ธ์Šคํ„ด์Šค ์ƒํƒœ๊ฐ€ ์ž์ฃผ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ ์ด ๋ชจ๋“œ๊ฐ€ ํ™œ์„ฑํ™”๋œ๋‹ค๋ฉด ์ค‘๋‹จ๋œ ์„œ๋น„์Šค์˜ ๋“ฑ๋ก์ด ๊ณ„์† ์œ ์ง€๋˜์–ด ๋‹ค๋ฅธ ์„œ๋น„์Šค๊ฐ€ ํ•ด๋‹น ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•  ๋•Œ ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋Œ€์‹  ์ด ๊ฐ’์„ false๋กœ ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋  ๊ฒƒ์ด๋‹ค.

THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTEXT INSTANCE EXPIRY 
IN CASE OF NETWORK/OTHER PROBLEMS.

์œ ๋ ˆ์นด ํ™•์žฅํ•˜๊ธฐ(์œ ๋ ˆ์นด ํ”„๋กœ์ ํŠธ 2 ์ƒ์„ฑ)

๋‘ ๊ฐœ ์ด์ƒ์˜ ์œ ๋ ˆ์นด ์ธ์Šคํ„ด์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ application.yml ํŒŒ์ผ์— ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์”ฉ ํ”„๋กœํŒŒ์ผ์„ ์‚ฌ์šฉํ•ด์„œ ์œ ๋ ˆ์นด๋ฅผ ๋‘๋ฒˆ ์‹œ์ž‘์‹œํ‚จ๋‹ค.

# ๊ธฐ๋ณธ ํ”„๋กœํŒŒ์ผ(๊ณตํ†ต)
eureka:
  client:
#    registerWithEureka: false ์‚ญ์ œ
#    fetchRegistry: false ์‚ญ์ œ
    serviceUrl:
      defaultZone: http://${other.eureka.host}:${other.eureka.port}/eureka/
---
# ํ”„๋กœํŒŒ์ผ1
spring:
  profiles: eureka-1
  application:
    name: eureka-1

server:
  port: 8761

eureka:
  instance:
    hostname: eureka1.tacocloud.com

# ํ”„๋ ˆ์ž„์›Œํฌ์™€ ์ƒ๊ด€์—†์ด ๊ณตํ†ต ํ”„๋กœํŒŒ์ผ์˜ ๋ณ€์ˆ˜๊ฐ’์„ ๋Œ€์ฒดํ•˜๊ธฐ ์œ„ํ•œ ์šฉ๋„
other:
  eureka:
    host: eureka2.tacocloud.com
    port: 8762

---
# ํ”„๋กœํŒŒ์ผ2
spring:
  profiles: eureka-2
  application:
    name: eureka-2

server:
  port: 8762

eureka:
  instance:
    hostname: eureka2.tacocloud.com

# ํ”„๋ ˆ์ž„์›Œํฌ์™€ ์ƒ๊ด€์—†์ด ๊ณตํ†ต ํ”„๋กœํŒŒ์ผ์˜ ๋ณ€์ˆ˜๊ฐ’์„ ๋Œ€์ฒดํ•˜๊ธฐ ์œ„ํ•œ ์šฉ๋„
other:
  eureka:
    host: eureka1.tacocloud.com
    port: 8762

์„œ๋น„์Šค ๋“ฑ๋กํ•˜๊ณ  ์ฐพ๊ธฐ

์ง€๊ธˆ๊นŒ์ง€ ์œ ๋ ˆ์นด ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•˜๊ณ  ๋“ฑ๋กํ–ˆ๋‹ค๋ฉด ์ด์ œ ์œ ๋ ˆ์นด ์„œ๋น„์Šค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋“ฑ๋กํ•  ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ๊ตฌ์„ฑํ•ด๋ณด๊ฒ ๋‹ค.

1. ์œ ๋ ˆ์นด ์„œ๋น„์Šค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋“ฑ๋กํ•˜๊ธฐ

ingredient-service ํ”„๋กœ์ ํŠธ์— ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

pom.xml ํŒŒ์ผ์—์„œ ๋งˆ์šฐ์Šค ์˜ค๋ฅธ์ชฝ ํด๋ฆญ > Spring - Edit Starters ํด๋ฆญ > Spring Cloud Discovery - Spring Discovery Client ์„ ํƒ ํ›„ OK

// pom.xml
</dependencies>
	<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>
				spring-cloud-starter-netflix-eureka-client
			</artifactId>
	</dependency>
</dependencies>
// spring cloud version๋„ ์ถ”๊ฐ€๋จ.
<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Finchley.SR1</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
</dependencyManagement>

์œ ๋ ˆ์นด ํด๋ผ์ด์–ธํŠธ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์œ ๋ ˆ์นด๋ฅผ ์ด์šฉํ•ด ์„œ๋น„์Šค๋ฅผ ์ฐพ๋Š”๋ฐ ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฒƒ์ด ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋œ๋‹ค. (์œ ๋ ˆ์นด์˜ ํด๋ผ์ด์–ธํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ๋ฆฌ๋ณธ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ ๋“ฑ)

์ด๋ ‡๊ฒŒ ๋˜๋ฉด ์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ ๋ ˆ์นด ์„œ๋น„์Šค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์˜ ํด๋ผ์ด์–ธํŠธ๋กœ ํ™œ์„ฑํ™”์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘๋˜๋ฉด 8761ํฌํŠธ๋กœ ์„œ๋น„์Šคํ•˜๋Š” ์œ ๋ ˆ์นด ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜๊ณ , UNKNOWN์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์œ ๋ ˆ์นด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ž์‹ ์„ ๋“ฑ๋กํ•œ๋‹ค.

์œ ๋ ˆ์นด ํด๋ผ์ด์–ธํŠธ ์†์„ฑ ๊ตฌ์„ฑ

์„œ๋น„์Šค์˜ ๊ธฐ๋ณธ ์ด๋ฆ„์ธ UNKNOWN์„ ๊ทธ๋Œ€๋กœ ๋‘๋ฉด ์œ ๋ ˆ์นด ์„œ๋ฒ„์— ๋“ฑ๋ก๋˜๋Š” ๋ชจ๋“  ์„œ๋น„์Šค ์ด๋ฆ„์ด ๊ฐ™๊ฒŒ ๋˜๋ฏ€๋กœ ๋ณ€๊ฒฝํ•ด๋ณด์ž.

spring:
    application:
        name: ingredient-service

์ด์ œ ์œ ๋ ˆ์นด ๋Œ€์‹œ๋ณด๋“œ์— ingredient-service ๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์„œ๋น„์Šค๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค.

server:
    port: 0

์„œ๋น„์Šค ํฌํŠธ์˜ ์ถฉ๋Œ์„ ๋ง‰๊ธฐ์œ„ํ•ด ํฌํŠธ๋ฅผ 0์œผ๋กœ ์„ค์ •ํ•œ๋‹ค. ์„œ๋ฒ„ ํฌํŠธ๋ฅผ 0์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ๊ฐ ์„œ๋น„์Šค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘๋  ๋•Œ ํฌํŠธ ๋ฒˆํ˜ธ๊ฐ€ ๋ฌด์ž‘์œ„๋กœ ์„ ํƒ๋œ๋‹ค.

eureka:
        client:
        service-url:
            defaultZone: http://eureka1.tacocloud.com:8761/eureka/,
                         http://eureka2.tacocloud.com:8762/eureka/

์œ„ url์˜ ํฌํŠธ๋กœ ๋ฆฌ์Šค๋‹ํ•˜๋Š” ์œ ๋ ˆ์นด ์„œ๋ฒ„์— ๋“ฑ๋ก๋˜๋„๋ก ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ตฌ์„ฑ๋œ๋‹ค. ์žฅ์•  ๋Œ€๋น„๋ฅผ ์œ„ํ•ด ๋‘ ๊ฐœ์˜ ์œ ๋ ˆ์นด ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋˜๋ฉด ์ฒซ ๋ฒˆ์งธ ์œ ๋ ˆ์นด ์„œ๋ฒ„์— ๋“ฑ๋ก์„ ์‹œ๋„ํ•˜๊ณ , ์‹คํŒจํ•˜๋ฉด ๋‘๋ฒˆ์งธ์— ํ”ผ์–ด(peer)๋กœ ์ง€์ •๋œ ์œ ๋ ˆ์นด ์„œ๋ฒ„์˜ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋“ฑ๋ก์„ ์‹œ๋„ํ•˜๊ฒŒ ๋œ๋‹ค.

2. ์„œ๋น„์Šค ์‚ฌ์šฉํ•˜๊ธฐ

์œ ๋ ˆ์นด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปจ์Šˆ๋จธ ์ฝ”๋“œ์— URL์„ ํ•˜๋“œ์ฝ”๋”ฉํ•˜๋Š” ๋Œ€์‹  ์„œ๋น„์Šค๋ช…์„ ์ ์œผ๋ฉด ๋œ๋‹ค.

๋•๋ถ„์— ์‚ฌ์šฉ๋˜๋Š” ์„œ๋น„์Šค์˜ ํŠน์ • ์ธ์Šคํ„ด์Šค์™€ ํ•ด๋‹น ์ปจ์Šˆ๋จธ๊ฐ€ ๋ฐ€์ ‘ํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜๋Š” ๊ฒƒ์„ ๋ง‰๊ณ , ์‚ฌ์šฉ๋˜๋Š” ์„œ๋น„์Šค์˜ ํฌ์ŠคํŠธ๋‚˜ ํฌํŠธ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด๋„ ํ•ด๋‹น ์ปจ์Šˆ๋จธ๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•˜๋„๋ก ํ•˜๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

์ปจ์Šˆ๋จธ๊ฐ€ ์ฐพ์€ ์„œ๋น„์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ผ ๊ฒฝ์šฐ์—๋Š”?

์ปจ์Šˆ๋จธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ธ์Šคํ„ด์Šค๋ฅผ ์„ ํƒํ•  ํ•„์š”์—†์ด ๋ฆฌ๋ณธ ํด๋ผ์ด์–ธํŠธ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๊ฐ€ ํ•˜๋‚˜์˜ ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์„ ํƒํ•ด์ค€๋‹ค.

์œ ๋ ˆ์นด์—์„œ ์ฐพ์€ ์„œ๋น„์Šค๋ฅผ ์„ ํƒ ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค

A. ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ๋œ RestTemplate

B. Feign์—์„œ ์ƒ์„ฑ๋œ ํด๋ผ์ด์–ธํŠธ ์ธํ„ฐํŽ˜์ด์Šค

์ด ์ค‘ ์–ด๋А ๋ฐฉ๋ฒ•์„ ์„ ํƒํ• ์ง€๋Š” ๊ฐœ์ธ์˜ ์ทจํ–ฅ์— ๋‹ฌ๋ ค์žˆ๋‹ค.

A-1. RestTemplate ์‚ฌ์šฉํ•ด ์„œ๋น„์Šค ์‚ฌ์šฉํ•˜๊ธฐ

before

public Ingredient getIngredientById(String ingredientId) {
    return rest.getForObject(
        "http://localhost:8080/ingredients/{id}", 
        Ingredient.class, ingredientId);
}

after

// RestTemplateConfig.java
@Configuration
@Conditional(NotFeignAndNotWebClientCondition.class)
public class RestTemplateConfig {

  @Bean
  @LoadBalanced
  public RestTemplate restTemplate() {
    return new RestTemplate();
  }
...

// IngredientServiceClient.java
@Component
public class IngredientServiceClient {

  private RestTemplate rest;
  
  public IngredientServiceClient(@LoadBalanced RestTemplate rest) {
    this.rest = rest;
  }
  
  public Ingredient getIngredientById(String ingredientId) {
    return rest.getForObject(
        "http://ingredient-service/ingredients/{id}", 
        Ingredient.class, ingredientId);
  }
...
}

์Šคํ”„๋ง RestTemplate์—์„œ HTTP GET ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•  ๋•Œ๋Š” before์ฒ˜๋Ÿผ URL์ด ํ•˜๋“œ์ฝ”๋”ฉ ๋˜์–ด์žˆ์—ˆ๋‹ค.

RestTemplateConfig.java

๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ๋œ RestTemplate ๋นˆ์„ ์„ ์–ธํ•˜๊ณ , @Bean @LoadBalanced ์„ ๋ฉ”์„œ๋“œ์— ์ง€์ •ํ•œ๋‹ค.

@LoadBalanced ๋Š” ๋‘ ๊ฐ€์ง€ ์˜๋ฏธ๋ฅผ ์ง€๋‹Œ๋‹ค.

  1. ํ˜„์žฌ์˜ RestTemplate์ด ๋ฆฌ๋ณธ์„ ํ†ตํ•ด์„œ๋งŒ ์„œ๋น„์Šค๋ฅผ ์ฐพ๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์Šคํ”„๋ง ํด๋ผ์šฐ๋“œ์— ์•Œ๋ ค์ค€๋‹ค
  2. ์ฃผ์ž… ์‹๋ณ„์ž๋กœ ๋™์ž‘ํ•œ๋‹ค. ์ฃผ์ž… ์‹๋ณ„์ž๋Š” ์„œ๋น„์Šค ์ด๋ฆ„์œผ๋กœ, getForObject()์˜ HTTP ์š”์ฒญ์—์„œ ํ˜ธ์ŠคํŠธ์™€ ํฌํŠธ ๋Œ€์‹  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

IngredientServiceClient.java

์‹์ž์žฌ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ๋œ RestTemplate๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์‹œ์ด๋‹ค.

before๊ณผ ๋‹ฌ๋ผ์ง€๋Š” ์ ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  1. ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ๋œ RestTemplate์„ ํ•„์š”๋กœ ํ•˜๋Š” ๋นˆ์— ์ฃผ์ž…ํ•˜๊ณ ,
  2. getIngredientById() ๋ฉ”์„œ๋“œ์— ํ˜ธ์ŠคํŠธ์™€ ํฌํŠธ ๋Œ€์‹  ํ•ด๋‹น ์„œ๋น„์Šค๋ช…์„ ์‚ฌ์šฉํ•˜๋„๋ก ๋ณ€๊ฒฝํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ๋˜๋ฉด (๋™์ž‘์›๋ฆฌ) ๋‚ด๋ถ€์ ์œผ๋กœ ingredient-service ๋ผ๋Š” ์„œ๋น„์Šค ์ด๋ฆ„์„ ์ฐพ์•„์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ์„ ํƒํ•˜๋„๋ก RestTemplate์ด ๋ฆฌ๋ณธ์— ์š”์ฒญํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์„ ํƒ๋œ ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค์˜ ํ˜ธ์ŠคํŠธ์™€ ํฌํŠธ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๋„๋ก ๋ฆฌ๋ณธ์ด URL์„ ๋ณ€๊ฒฝํ•œํ›„ ์›๋ž˜๋Œ€๋กœ RestTemplate์ด ์‚ฌ์šฉ๋œ๋‹ค.

ingredient-service ์„œ๋น„์Šค์˜ ์ปจํŠธ๋กค๋Ÿฌ(IngredientController.java)

@Controller
@RequestMapping("/ingredients")
public class IngredientController {

  private IngredientServiceClient client;

  public IngredientController(IngredientServiceClient client) {
    this.client = client;
  }
  
...

  @GetMapping("/{id}")
  public String ingredientDetailPage(@PathVariable("id") String id, Model model) {
    log.info("Fetched an ingredient from a RestTemplate-based service.");
    model.addAttribute("ingredient", client.getIngredientById(id));
    return "ingredientDetail";
  }
  
}

A-2. WebClient๋กœ ์„œ๋น„์Šค ์‚ฌ์šฉํ•˜๊ธฐ

11์žฅ์—์„œ ๋ฐฐ์šด WebClient๊ฐ€ HTTP ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์•˜๋‹ค. ๋ฆฌ์•กํ‹ฐ๋ธŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๊ด€์‹ฌ์ด ๋งŽ๋‹ค๋ฉด RestTemplate ๋Œ€์‹  WebClient๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Flux์™€ Mono๊ฐ™์€ ๋ฆฌ์•กํ‹ฐ๋ธŒ ํƒ€์ž…๊ณผ ํ•จ๊ป˜ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด, RestTemplate ์„ ์‚ฌ์šฉํ–ˆ๋˜ ๊ฒƒ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ WebClient๋ฅผ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ๋œ ํด๋ผ์ด์–ธํŠธ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// WebClientConfig.java
@Configuration
public class WebClientConfig {

  @Bean
  @LoadBalanced
  public WebClient.Builder webClientBuilder() {
    return WebClient.builder();
  }
...
}

// IngredientServiceClient.java
@Service
@Profile("webclient")
public class IngredientServiceClient {

  private WebClient.Builder wcBuilder;
  
  public IngredientServiceClient(@LoadBalanced WebClient.Builder wcBuilder) {
    this.wcBuilder = wcBuilder;
  }
  
  public Mono<Ingredient> getIngredientById(String ingredientId) {  
    return wcBuilder.build()    
        .get()      
        .uri("http://ingredient-service/ingredients/{id}", ingredientId)    
        .retrieve().bodyToMono(Ingredient.class);
  }

B. Feign ํด๋ผ์ด์–ธํŠธ ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜ํ•˜๊ธฐ

Feign ์€ REST ํด๋ผ์ด์–ธํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฉฐ, ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ด REST ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ •์˜ํ•œ๋‹ค.

๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ, ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฆฌํผ์ง€ํ„ฐ๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ž๋™์œผ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌํ•œ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.

์˜์กด์„ฑ ์ถ”๊ฐ€

<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-openfeign</artifactId>
		</dependency>
</dependencies>

Feign ํ™œ์„ฑํ™”

@Configuration
@EnableFeignClients
public class FeignClientConfig {
}

IngredientClient.java ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜

package tacos.ingredientclient.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import tacos.ingredientclient.Ingredient;

@FeignClient("ingredient-service")
public interface IngredientClient {

  @GetMapping("/ingredients/{id}")
  Ingredient getIngredient(@PathVariable("id") String id);
...
}

ingredient-service๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์œ ๋ ˆ์นด์— ๋“ฑ๋ก๋œ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•ด ์‹์ž์žฌ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ž‘์„ฑํ•˜์˜€๋‹ค.

์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์ •์˜ํ•˜๋ฉด Feign์ด ๋Ÿฐํƒ€์ž„์‹œ์— ์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ฐพ๋Š”๋‹ค. ๊ทธ๋ฆฌ๊ณ  Feign์ด ์ž๋™์œผ๋กœ ๊ตฌํ˜„ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ์— ๋นˆ์œผ๋กœ ๋…ธ์ถœ์‹œํ‚จ๋‹ค.

@FeignClient ์–ด๋…ธํ…Œ์ด์…˜์€ ํ•ด๋‹น ํด๋ž˜์Šค ๋‚ด์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ๊ฐ€ ingredient-service์— ๋Œ€ํ•œ ์š”์ฒญ์ž„์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ ingredieng-service๋Š” ๋ฆฌ๋ณธ์ด ์ฐพ๊ฒŒ ๋œ๋‹ค.

@GetMapping ์–ด๋…ธํ…Œ์ด์…˜์€ ๋ฆฌ๋ณธ์ด ์„ ํƒํ•œ ํ˜ธ์ŠคํŠธ์™€ ํฌํŠธ์˜ /ingredients/{id} ๊ฒฝ๋กœ๋กœ HTTP GET ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.

ingredient-service ์„œ๋น„์Šค์˜ ์ปจํŠธ๋กค๋Ÿฌ(IngredientController.java)

RestTemplate๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์™€ ๊ฐ™๋‹ค.

@Controller
@RequestMapping("/ingredients")
public class IngredientController {

  private IngredientClient client;

	@Autowired
  public IngredientController(IngredientClient client) {
    this.client = client;
  }
  
...
  
  @GetMapping("/{id}")
  public String ingredientDetailPage(@PathVariable("id") String id, Model model) {
    log.info("Fetched an ingredient from a Feign client.");
    model.addAttribute("ingredient", client.getIngredient(id));
    return "ingredientDetail";
  }
  
}

์ถ”๊ฐ€. Feign์—๋Š” ์ž์‹ ์˜ ์–ด๋…ธํ…Œ์ด์…˜์ธ @RequestLine ๊ณผ @Param ์ด ์žˆ๋Š”๋ฐ, ์Šคํ”„๋ง MVC์˜ @RequestMapping, @PathVariable๊ณผ ๊ฑฐ์˜ ์œ ์‚ฌํ•˜๋‹ค.

Feign, WebClient, RestTemplate ์ด์ค‘ ์–ด๋А ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋“  REST ํด๋ผ์ด์–ธํŠธ์—์„œ ํŠน์ • ํ˜ธ์ŠคํŠธ ์ด๋ฆ„, ํฌํŠธ๋ฅผ ํ•˜๋“œ์ฝ”๋”ฉํ•˜๋Š” ๊ฒƒ์„ ๋ง‰์•„์ค€๋‹ค.

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