์ฑ„ํŒ… ์„œ๋ฒ„ ๊ตฌ์ถ•๊ธฐ 1 | Elixir Socket Server

kimsg
  • #fromm
  • #์ฑ„ํŒ…
  • #socket
  • #Elixir
  • #Phoenix

๋“ค์–ด๊ฐ€๋ฉฐ

ํ”„๋กฌ ์„œ๋น„์Šค ์†Œ๊ฐœ ์ด๋ฏธ์ง€

fromm ์„œ๋น„์Šค๋ฅผ ์‹œ์ž‘ํ•œ์ง€๋„ 1๋…„์ด ๋„˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ž‘๊ณ  ๋น ๋ฅธ ์‹คํ–‰๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค๋ฅผ ์ง€ํ–ฅํ•˜๋Š” ํŒ€์˜ ํŠน์„ฑ์ƒ ์„œ๋น„์Šค ์ดˆ๊ธฐ์—๋Š” ๋ชจ๋“  ๊ฒƒ์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ฐœ๋ฐœํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. fromm ์„œ๋น„์Šค์˜ ํ•ต์‹ฌ์ด ๋˜๋Š” ์ฑ„ํŒ… ์„œ๋ฒ„์˜ ๊ฒฝ์šฐ์—๋„ ๊ตฌ๊ธ€์˜ Firestore๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น ๋ฅด๊ฒŒ ๋น„์ฆˆ๋‹ˆ์Šค๋ฅผ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

1๋…„ ๋™์•ˆ ์„œ๋น„์Šค์˜ ๊ธ‰๊ฒฉํ•œ ์„ฑ์žฅ๊ณผ ๋”๋ถˆ์–ด ํŠธ๋ž˜ํ”ฝ ์ฆ๊ฐ€์™€ ๋‹ค์–‘ํ•œ ํ•ด์™ธ ์œ ์ €์˜ ์ฆ๊ฐ€๋ฅผ ๊ฒฝํ—˜ํ–ˆ์Šต๋‹ˆ๋‹ค. Firestore๋กœ๋Š” ๊ฐ๋‹นํ•˜๊ธฐ ์–ด๋ ค์šด ํŠธ๋ž˜ํ”ฝ ์ˆ˜์น˜๋ฅผ ๋ณด์ผ ๋•Œ๋„ ์žˆ์—ˆ๊ณ , ์ค‘๊ตญ์—์„œ๋Š” Firestore๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๋“ฑ ๋‹ค์–‘ํ•œ ์ด์Šˆ์™€ ๋งˆ์ฃผํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์— ํŒ€ ๋‚ด๋ถ€์—์„œ๋Š” ๋‹ค๋ฅธ ์†”๋ฃจ์…˜์€ ์—†๋Š”์ง€ ์ง์ ‘ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒƒ์ด ์ตœ์„ ์ผ์ง€ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฉด์œผ๋กœ ๊ฒ€ํ† ๋ฅผ ์ง„ํ–‰ํ–ˆ๊ณ , ๊ฒฐ๊ตญ ์ง์ ‘ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๊ฒŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ๊ธ€ ์—์„œ๋Š” ์ฑ„ํŒ…๊ณผ ๊ด€๋ จ๋œ ์ž‘์—… ์ค‘, ์†Œ์ผ“ ์„œ๋ฒ„ ๊ตฌ์ถ•์„ ํ•˜๋ฉฐ ๊ฒช์—ˆ๋˜ ๋‚ด์šฉ์„ ๊ณต์œ ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

Elixir ?

์ฒซ ๋ฒˆ์งธ๋กœ ๊ณ ๋ฏผํ–ˆ๋˜ ๋ถ€๋ถ„์€ ์–ธ์–ด์™€ ํ”„๋ ˆ์ž„์›Œํฌ ์˜€์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ fromm ๋ฐฑ์—”๋“œ์˜ ๋Œ€๋ถ€๋ถ„์€ NodeJS ๊ธฐ๋ฐ˜์— Typescript๋กœ ์ž‘์„ฑ๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. NodeJS๋„ ๊ต‰์žฅํžˆ ์ข‹์€ ์„ ํƒ์ง€์ด๊ณ  ๋ชจ๋“  ํŒ€์›๋“ค์ด ์ต์ˆ™ํ•˜๋‹ค๋Š” ์ ์—์„œ ์žฅ์ ์ด ์žˆ์—ˆ์ง€๋งŒ, ์ข€ ๋” ๋ชฉ์ ์— ๋งž๋Š” ์–ธ์–ด๋Š” ์—†์„๊นŒ ์—ด์‹ฌํžˆ ์กฐ์‚ฌ๋ฅผ ํ•˜๋˜ ์ค‘์— Elixir๋ฅผ ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Elixir is a dynamic, functional language for building scalable and maintainable applications. (elixir-lang.org)

Elixir๋Š” ๊ณ ์„ฑ๋Šฅ ๋ถ„์‚ฐ Erlang VM (BEAM) ์—์„œ ๋Œ์•„๊ฐ€๋Š” Josรฉ Valim์ด ๊ฐœ๋ฐœํ•œ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์ž…๋‹ˆ๋‹ค. Erlang์€ ์ „ํ™” ๊ตํ™˜ ์žฅ๋น„์—์„œ ๋Œ€ํ˜• ํ†ต์‹  ์‹œ์Šคํ…œ์— ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด 1986๋…„์— Ericsson์‚ฌ์—์„œ ๊ฐœ๋ฐœ๋œ ์–ธ์–ด์ž…๋‹ˆ๋‹ค. ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ •์ ์ธ ์‹ค์‹œ๊ฐ„ ๋™์‹œ ์‹œ์Šคํ…œ์„ ์œ„ํ•ด ๊ณ ์•ˆ๋œ ์–ธ์–ด์ธ ๋งŒํผ ๋‹ค์–‘ํ•œ ํ†ต์‹  ์‹œ์Šคํ…œ๊ณผ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ Facebook, Rabbit MQ, Whatsapp ๋“ฑ์—์„œ ์ฑ„ํŒ…๊ณผ ๋ฉ”์‹œ์ง• ์ฒ˜๋ฆฌ๋กœ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

Erlang์ด ์˜ค๋ž˜์ „์— ๊ฐœ๋ฐœ๋œ ์–ธ์–ด๋‹ค ๋ณด๋‹ˆ, ํ˜„๋Œ€์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์— ๋งž๊ฒŒ ํƒ„์ƒํ•œ ๊ฒƒ์ด Elixir ์ž…๋‹ˆ๋‹ค. ๋งˆ์น˜ Java์™€ Kotlin์˜ ๊ด€๊ณ„์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.

๋ญ”๊ฐ€ ์ƒˆ๋กœ์šด ๊ฒƒ์ด๋ผ ํฅ๋ฏธ๋กญ๊ธฐ๋„ํ•˜๊ณ , ์„œ๋น„์Šค๊ฐ€ ์š”๊ตฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์™„๋ฒฝํ•˜๊ฒŒ ๊ฐ–์ถ”๊ณ  ์žˆ์–ด์„œ Elixir๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

Elixir์˜ ์ธ๊ธฐ Elixir์˜ ์ธ๊ธฐ (Stackoverflow Programming, scripting and markup languages)

Phoenix Framework

Elixir ์ž์ฒด๋กœ๋„ ์ข‹์ง€๋งŒ, ์‰ฝ๊ณ  ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์œผ๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž˜ ๋งŒ๋“ค์–ด์ง„ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. Phoenix๋Š” Elixir๋กœ ์ž‘์„ฑ๋œ ๋…๋ณด์ ์ธ ํ”„๋ ˆ์ž„์›Œํฌ๋ผ ๋ฐ”๋กœ ์ฐพ์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

Phoenix is a web development framework written in Elixir (phoenixframework.org)

Phoenix๋Š” Josรฉ Valim์ด Elixir๋กœ ๋งŒ๋“  ์›น ํ”„๋ ˆ์ž„์›Œํฌ ์ž…๋‹ˆ๋‹ค. Ruby์˜ ํ•ต์‹ฌ ๊ฐœ๋ฐœ์ž์˜€๋˜ ๊ทธ๊ฐ€ Ruby on Rails์™€ Phython Django์— ์˜๊ฐ์„ ์–ป์–ด ๋งŒ๋“ค์—ˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

Phoenix์˜ ์ธ๊ธฐ Phoenix์˜ ์ธ๊ธฐ (Stackoverflow Web frameworks and technologies)

Phoenix๋Š” ์ž์ฒด PubSub์„ ์‚ฌ์šฉํ•˜์—ฌ Channel, Presence ๋“ฑ์˜ ๊ธฐ๋Šฅ์ด ์ž˜ ์ถ”์ƒํ™” ๋˜์–ด์žˆ์–ด์„œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋‹ค์–‘ํ•œ ์–ธ์–ด๋กœ Client SDK๋ฅผ ์ œ๊ณตํ•˜์—ฌ ์›น, Android, iOS ์—์„œ๋„ ์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

Local/Remote PubSub์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณดํ†ต ๋ถ„์‚ฐ ์†Œ์ผ“ ์„œ๋ฒ„์—์„œ ์‚ฌ์šฉ๋˜๋Š” PubSub์šฉ Redis ๊ฐ™์€ ๊ฒƒ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. ๋‹น์žฅ์€ Phoenix์—์„œ ์ œ๊ณตํ•˜๋Š” Local/Remote PubSub๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์œผ๋‚˜, ์ด ๋ถ€๋ถ„์€ ์ถ”ํ›„์— ๋‹ค์–‘ํ•œ ์šด์˜ํ™˜๊ฒฝ์—์„œ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ํ•ด๋ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ RedisAdapter๋ฅผ ์ œ๊ณตํ•˜์—ฌ Redis๋ฅผ PubSub์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๋‹ค๋งŒ ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ๊ฐ ๋…ธ๋“œ๋“ค์„ ์—ฐ๊ฒฐํ•ด์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ, ์ด๋Š” libcluster ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•ด๊ฒฐํ•ด์ค๋‹ˆ๋‹ค. libcluster๋Š” ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ์—์„œ ๋…ธ๋“œ๋“ค์„ ์ฐพ์•„๋‚ด๊ณ  ์—ฐ๊ฒฐ์„ ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์—ฐ๊ฒฐ์ด ๋œ ์ดํ›„์—๋Š” PubSub์„ ํ†ตํ•ด ๋ฉ”์‹œ์ง€๋ฅผ broadcastํ•˜๋ฉด ์•Œ์•„์„œ ์ „์ฒด ๋…ธ๋“œ์— ๋ฉ”์‹œ์ง€๊ฐ€ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

Phoenix PubSub Local/Remote PubSub

Infra

์ด๋ ‡๊ฒŒ ๊ฐœ๋ฐœ๋œ ์†Œ์ผ“ ์„œ๋ฒ„ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์–ด๋–ค ์ธํ”„๋ผ์—์„œ ์šด์˜ํ• ์ง€ ๊ณ ๋ฏผ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์›๋”์›”์—์„œ๋Š” AWS๋ฅผ ๋ฉ”์ธ ์ธํ”„๋ผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. AWS์—์„œ ์†Œ์ผ“์„œ๋ฒ„๋ฅผ ์šด์˜ํ•˜๊ธฐ์— EKS์™€ ECS๊ฐ€ ๋ฌด๋‚œํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๊ณ  ํŒ€๋‚ด ๋…ธํ•˜์šฐ๊ฐ€ ๋” ๋งŽ์€ ECS๋ฅผ ์ตœ์ข… ์„ ํƒํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Remote PubSub์„ ์œ„ํ•ด ECS task(๋…ธ๋“œ) ๊ฐ„์— ์—ฐ๊ฒฐ์„ ํ•ด์ค˜์•ผํ•ฉ๋‹ˆ๋‹ค. task์˜ IP ์ฃผ์†Œ๋ฅผ ์•Œ์•„์•ผ libcluster๊ฐ€ ์—ฐ๊ฒฐ์„ ํ•ด์ค„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Service Discovery๋ฅผ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. Route53์— private DNS๋ฅผ ์ƒ์„ฑํ•˜๊ณ  libcluster์—์„œ DNS query๋ฅผ ํ†ตํ•ด ๊ฐ task์˜ IP ์ฃผ์†Œ๋ฅผ ์•Œ์•„๋‚ด ์—ฐ๊ฒฐ์„ ๋งบ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

AWS ECS Service Discovery Service Discovery

๋˜ํ•œ ์†Œ์ผ“ ์„œ๋ฒ„์˜ ๊ณ ์„ฑ๋Šฅ์ด ์š”๊ตฌ๋˜๋Š” ์ƒํ™ฉ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋ถ€ํ•˜ ๋ถ„์‚ฐ์— ์ ํ•ฉํ•˜๋ฉฐ ๊ฐ‘์ž‘์Šค๋Ÿฌ์šด ํŠธ๋ž˜ํ”ฝ ์ฆ๋Œ€์— ์ตœ์ ํ™” ๋˜์–ด์žˆ๋Š” Network Load Balancer๋ฅผ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ถ€ํ•˜ํ…Œ์ŠคํŠธ

์œ„์™€ ๊ฐ™์ด ๊ตฌ์„ฑ ํ›„ ์‹ค์ œ ์–ด๋Š ์ •๋„์˜ ํŠธ๋ž˜ํ”ฝ์„ ๊ฐ๋‹นํ•  ์ˆ˜ ์žˆ์„์ง€ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ํด๋ผ์ด์–ธํŠธ์—์„œ ์†Œ์ผ“ ์—ฐ๊ฒฐ์„ ์š”์ฒญํ•˜๋Š” socket connection ์„ฑ๋Šฅ ์ธก์ •๊ณผ, ์—ฐ๊ฒฐ๋œ ์†Œ์ผ“์— ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด ์„œ๋ฒ„์—์„œ ํ˜ธ์ถœํ•˜๋Š” broadcast API ์„ฑ๋Šฅ ์ธก์ •์œผ๋กœ ๋‚˜๋ˆ„์–ด ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

Socket Connection

์ดˆ๋‹น 1,000๊ฐœ์˜ ์š”์ฒญ x 100์ดˆ = ์ด 100,000๊ฐœ์˜ connection

Fargate 4 vCPU x 2 task

  • connection time (P95): 100ms
  • peak cpu: 66%
  • peak memory: 30%

์ฃผ๋ชฉํ•  ๋งŒํ•œ ์ ์€ ์ดˆ๋‹น ์š”์ฒญ์ด ์ฆ๊ฐ€ํ• ์ˆ˜๋ก peak cpu๊ฐ€ ๋†’์•„์ง€๊ณ , ์ด connection ๊ฐœ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ• ์ˆ˜๋ก peak memory๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค๋Š”์  ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

broadcast API

์†Œ์ผ“ 15,000๊ฐœ ์—ฐ๊ฒฐ๋œ ์ƒํƒœ์—์„œ ์ดˆ๋‹น 10๊ฐœ์˜ ์š”์ฒญ x ์š”์ฒญ๋‹น ๋ฉ”์‹œ์ง€ 3,000๊ฐœ = ์ดˆ๋‹น ์ด 30,000๊ฐœ์˜ ๋ฉ”์‹œ์ง€

์š”์ฒญ๋‹น ๋ฉ”์‹œ์ง€์˜ ์ˆ˜๊ฐ€ ์–ด๋Š ์ˆ˜์ค€์„ ๋„˜์–ด์„œ๋„ ์‘๋‹ต์†๋„๊ฐ€ ๋Š๋ ค์ง€๊ณ , ์š”์ฒญ๋‹น ๋ฉ”์‹œ์ง€์˜ ์ˆ˜๋ฅผ ์ค„์ด๋Š” ๋Œ€์‹  ์ดˆ๋‹น ์š”์ฒญ์„ ๋Š˜๋ ค๋„ ๋Š๋ ค์ ธ์„œ ์ ์ • ์ˆ˜์ค€์„ ์ฐพ์•„์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

Fargate 4 vCPU x 20 task

  • response time (P95): 671ms
  • response time (P99): 713ms
  • peak cpu: 9%

Fargate 16 vCPU x 1 task

  • response time (P95): 383ms
  • response time (P99): 399ms
  • peak cpu: 16%

task๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ์ธ ๊ฒฝ์šฐ ์ด ์ŠคํŽ™์€ ๋” ๋†’์„ ์ˆ˜ ์žˆ์ง€๋งŒ, Remote PubSub์— ์‹œ๊ฐ„์ด ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— response time์€ ๋” ๋Š˜์–ด๋‚˜๊ฒŒ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ถ€๋ถ„์€ ์ถ”ํ›„์— ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ฐœ์„ ์‚ฌํ•ญ

์ง€๊ธˆ๊นŒ์ง€ Elixir์™€ Phoenix Framework๋ฅผ ํ™œ์šฉํ•œ ์†Œ์ผ“ ์„œ๋ฒ„ ๊ตฌ์ถ•ํ•œ ๊ฒƒ์„ ์ •๋ฆฌํ•ด๋ดค์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ธฐ๊ฐ„์ด๋ผ ํด๋ผ์ด์–ธํŠธ์— Firestore์™€ ํ•จ๊ป˜ ์ ์šฉํ•ด๋‘๊ณ  ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ์€ ๋‹จ์ˆœํ•˜๊ฒŒ ์†Œ์ผ“ ์—ฐ๊ฒฐ๋งŒ ํ•˜๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• ๋งŒ ํ•˜๊ณ  ์žˆ์ง€๋งŒ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๊ณ ๋„ํ™”๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  • ์„œ๋ฒ„ ๋ฐฐํฌ์‹œ ์†Œ์ผ“ ์—ฐ๊ฒฐ์ด ํ•œ ๋ฒˆ์— ๋Š์–ด์ง€๋Š” ์ด์Šˆ
  • ์—ฐ๊ฒฐ์ด ๋Š๊ฒผ๋‹ค๊ฐ€ ์žฌ์—ฐ๊ฒฐ ๋œ ๊ฒฝ์šฐ ๋ฉ”์‹œ์ง€ ๋ˆ„๋ฝ ์ด์Šˆ
  • ์ฑ„ํŒ… ์ˆœ์„œ ๋ณด์žฅ

ํ˜„์žฌ๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ ํ•ด๋‹น ์ด์Šˆ๋“ค์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์ง€๋งŒ, ๋ฐฑ์—”๋“œ์—์„œ๋„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฐจ๊ทผ์ฐจ๊ทผ ๊ฐœ๋ฐœํ•ด๋‚˜๊ฐ€์•ผ ๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์ดํ›„์— ๊ฐœ์„ ๋œ ๋ชจ์Šต์œผ๋กœ ๋‹ค์‹œ ์ฐพ์•„ ์˜ค๊ฒ ์Šต๋‹ˆ๋‹ค.

์ €ํฌ์™€ ํ•จ๊ป˜ ์žฌ๋ฐŒ๊ฒŒ ๊ฐœ๋ฐœํ•˜์‹ค ๋ถ„์„ ์ฐพ์Šต๋‹ˆ๋‹ค! ๊ด€์‹ฌ ์žˆ์œผ์‹  ๋ถ„์€ ์•„๋ž˜ ์ฑ„์šฉ ๊ณต๊ณ ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š” :)

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ

Art Changes Life

๋…ธ๋จธ์Šค์™€ ํ•จ๊ป˜ ์—”ํ„ฐํ…Œํฌ ์‚ฐ์—…์„ ํ˜์‹ ํ•ด๋‚˜๊ฐˆ ๋ฉค๋ฒ„๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ฑ„์šฉ ์ค‘์ธ ๊ณต๊ณ  ๋ณด๊ธฐ