๐Ÿ’ป Programming (358)

์•ˆ๋…•ํ•˜์„ธ์š”, ์˜ค๋žœ๋งŒ์— ๊ธ€์„ ์”๋‹ˆ๋‹ค.

 

์˜ค๋Š˜ ๊ณต์œ ๋“œ๋ฆฌ๋Š” ๋‚ด์šฉ์€ ๋ถ„์‚ฐ์‹œ์Šคํ…œ์—์„œ DLQ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ œ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์‹œ์Šคํ…œ ์ค‘ ํ•˜๋‚˜๊ฐ€ ์ฃผ๋กœ ์นดํ”„์นด ๋ฉ”์‹œ์ง€๋ฅผ ์ปจ์ˆจํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋Š” ์ผ์„ ํ•˜๋Š” ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์กด์žฌํ•˜๊ณ  ๋ชจ๋‘ ๋™์ผํ•œ ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ๋ฐฐํฌ๋˜์–ด์žˆ์ฃ .

์†Œ์Šค์ฝ”๋“œ์—๋Š” JPA๋ฅผ ์ด์šฉํ•œ CRUD ๋กœ์ง์ด ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ฌธ์ œ๊ฐ€ ์—†๋˜ ์‹œ์Šคํ…œ์ธ๋ฐ ๊ด€๋ จ๋ถ€์„œ์—์„œ ํŒŒํ‹ฐ์…˜ ๊ฐœ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๋ฉด์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.

๋‹จ์ˆœํžˆ ํŒŒํ‹ฐ์…˜ ๊ฐœ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚œ๊ฒŒ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ ๊ทธ์ชฝ์—์„œ ๋ณด๋‚ด๋Š” ์นดํ”„์นด ๋ฉ”์‹œ์ง€์˜ ํŠน์„ฑ๊ณผ๋„ ๊ด€๋ จ์ด ์žˆ์—ˆ์ฃ .

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

์˜ˆ๋ฅผ๋“ค์–ด ์ธ์Šคํ„ด์Šค 1๋ฒˆ์—์„œ {A, B} ๋ผ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ธ์Šคํ„ด์Šค 2๋ฒˆ์—์„œ {A,C}, 3๋ฒˆ ์ธ์Šคํ„ด์Šค์—์„œ {A,D} ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ฉด์„œ A ๋ฐ์ดํ„ฐ๋ฅผ 3๋Œ€์˜ ์ธ์Šคํ„ด์Šค์—์„œ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•˜๋ ค๊ณ  ํ•œ๊ฑฐ์ฃ . ์„ธ ๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋„ ๋“ค๊ณ ์žˆ์œผ๋ฏ€๋กœ ๋ฌด์กฐ๊ฑด ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค˜์•ผ ํ•˜๋Š” ๊ฒƒ๋“ค์ด์—ˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฑธ ์–ด๋–ป๊ฒŒ ํ•  ๊นŒ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€ SQS๋ฅผ DLQ๋ฅผ ์ด์šฉํ•˜์—ฌ Lambda ์™€ ์—ฐ๊ฒฐ์‹œ์ผœ ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚œ ๋’ค์— ์ˆ˜๋™๋™๊ธฐํ™” API๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ๊ตฌ์„ฑํ•˜๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•œ ์ด์œ ๋Š” ๊ฐ€์žฅ ๋น ๋ฅธ์‹œ๊ฐ„์— ์ถ”๊ฐ€์ ์ธ ์†Œ์Šค๊ตฌํ˜„ ์—†์ด ํšจ๊ณผ์ ์œผ๋กœ ๋ชฉ์ ์„ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋งŒ์•ฝ DLQ๋กœ ๋“ค์–ด์˜ค๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ๋งŽ์•„์ง„๋‹ค๋ฉด ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š”๊ฒŒ ๋น„์šฉ์ธก๋ฉด์—์„  ๋” ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

์ธ์Šคํ„ด์Šค๋Š” ์–ด์ฐจํ”ผ ๋– ์žˆ์–ด์•ผ ํ•˜๋Š”๊ฑฐ๊ณ  ๋žŒ๋‹ค๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ๋žŒ๋‹ค์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋น„์šฉ์ด ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

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

 

์•„๋ฌดํŠผ, SQS๋ฅผ DLQ๋กœ ์ด์šฉํ•˜๊ณ , Lambda ํ•จ์ˆ˜๋ฅผ ์—ฐ๊ฒฐ์‹œ์ผœ ์ผ์ • ์‹œ๊ฐ„๋’ค์— ์ˆ˜๋™API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‹คํŒจํ–ˆ๋˜ ๋ฉ”์‹œ์ง€์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ฝ์–ด์™€์„œ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

SQS Lambda Trigger ์„ค์ •

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•ด๋†“์œผ๋ฉด SQS ๋ฉ”์‹œ์ง€๊ฐ€ ๋ฐœํ–‰๋  ๋•Œ๋งˆ๋‹ค Lambda ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋Š”๋ฐ Lambda ํ•จ์ˆ˜์—์„œ๋Š” API๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๋žŒ๋‹ค ํŠธ๋ฆฌ๊ฑฐ์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ AWS์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค๋ช…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

You can configure your Amazon SQS queue to trigger an AWS Lambda function when new messages arrive in the queue.
Your queue and Lambda function must be in the same AWS Region.
A Lambda function can use at most one queue as an event source. You can use the same queue with multiple Lambda functions.
You can't associate an encrypted queue that uses the AWS-managed CMK for Amazon SQS with a Lambda function in a different AWS account.

์ €์˜ ๊ฒฝ์šฐ์—๋Š” ํ•„์š”์—†๊ธดํ•˜์ง€๋งŒ, ํ•˜๋‚˜์˜ SQS์— ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋žŒ๋‹คํ•จ์ˆ˜๋ฅผ ์—ฐ๊ฒฐํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ์ข‹์•„๋ณด์ด๋„ค์š”.

 

์ž, ๊ทธ๋ฆฌ๊ณ  SQS์™€ ์—ฐ๊ฒฐํ•œ Lambda ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import logging
import json
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError

logger = logging.getLogger()
logger.setLevel(logging.INFO)

API_URL = "https://company.com/employees/{}"

def lambda_handler(event, context):

    message = json.loads(event['Records'][0]['body'])
    
    url = API_URL.format(message['employeeId'])
    headers = {'Content-type':'application/json'}
    
    req = Request(url, headers=headers, data=b'', method='PUT')
    
    try:
        response = urlopen(req)
        return response.getcode()
    except HTTPError as e:
        logger.error("Request failed: %d %s, sqsBody:%s", e.code, e.reason, message)
        return e.code
    except URLError as e:
        logger.error("Server connection failed: %s, sqsBody:%s", e.reason, message)
    return None

Python์€ ๊ณต๋ถ€ํ•œ์  ์—†์œผ๋‚˜, ์ž๋ฐ”๋Š” ์ปดํŒŒ์ผ์–ธ์–ด๋ผ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ AWS์ฝ˜์†”์—์„œ ์ž‘์„ฑํ•  ์ˆ˜ ์—†์–ด์„œ python์œผ๋กœ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

 

๋‚ด์šฉ์€ ๋ณ„๊ฑฐ ์—†๊ณ  event์— SQS ๋ฉ”์‹œ์ง€์˜ ๋‚ด์šฉ์ด ๋“ค์–ด์˜ค๊ธฐ ๋•Œ๋ฌธ์— event์—์„œ ๋ฉ”์‹œ์ง€์˜ body ๋‚ด์šฉ์„ ์ถ”์ถœํ•˜์—ฌ message ๋ณ€์ˆ˜์— ๋„ฃ๊ณ  ์ด๊ฒƒ์„ API ํ˜ธ์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•œ๊ฒƒ์ด ๋‹ค์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” employeeId ๊ฐ’์ด SQS ๋ฉ”์‹œ์ง€์— {"employeeId":1234} ํ˜•ํƒœ๋กœ ๋“ค์–ด์žˆ์—ˆ๋‹ค๋ฉด, ์ €๊ธฐ์„œ 1234 ๊ฐ’์„ ์ถ”์ถœํ•˜์—ฌ api ํ˜ธ์ถœํ• ๋•Œ path variable๋กœ ๋„ฃ์–ด์„œ https://company.com/employees/1234 ๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ํ•œ๊ฑฐ์ฃ . 

 

์œ„ ์ฝ”๋“œ์ƒ์—์„  ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ทธ๋ƒฅ ๋กœ๊น…ํ•˜๊ฑฐ๋‚˜ ์—๋Ÿฌ์ฝ”๋“œ ๋ฆฌํ„ดํ•˜๋„๋ก ํ•ด๋†“์•˜๋Š”๋ฐ, ์—๋Ÿฌ๊ฑด์ด ๋งŽ์ด ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ์Šฌ๋ž™์•Œ๋žŒ์„ ๋ณด๋‚ด๋„๋ก ํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

 

์ด์ƒ์œผ๋กœ SQS์™€ Lambda๋ฅผ ์ด์šฉํ•œ ๋ถ„์‚ฐ์ฒ˜๋ฆฌ์‹œ์Šคํ…œ์—์„œ์˜ DLQ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ๊ธฐ๋ก์„ ๋งˆ์นฉ๋‹ˆ๋‹ค.

์‚ฌ์‹ค ์ด๊ฑธ git ๋ช…๋ น์–ด๋กœ ํ™•์ธํ•  ์ผ์ด ์–ผ๋งˆ๋‚˜ ์žˆ๊ฒ ๋ƒ๋งˆ๋Š” ๋‹ค์Œ์— ํ˜น์‹œ๋ผ๋„ ๋˜ ์“ธ์ผ์ด ์žˆ์„๊นŒ๋ด ๊ธฐ๋ก์šฉ์œผ๋กœ ๋‚จ๊น๋‹ˆ๋‹ค.

 

git shortlog -sne --since="01 Apr 2022" --before="01 May 2022" --no-merges

์œ„ ๋ช…๋ น์–ด๋ฅผ ์ด์šฉํ•˜๋ฉด 22๋…„ 4์›” 1์ผ๋ถ€ํ„ฐ 22๋…„ 5์›” 1์ผ ์ „๊นŒ์ง€, ์ฆ‰, 4์›” ํ•œ๋‹ฌ๋™์•ˆ merge ์ปค๋ฐ‹์„ ์ œ์™ธํ•œ ์‹ค์งˆ์ ์ธ ๊ฐœ๋ฐœ์ž‘์—…์˜ ์ปค๋ฐ‹๊ฑด์ˆ˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

 53  ํ™๊ธธ๋™ <gildong.hong@company.com>
 49  ๊น€์ฒ ์ˆ˜ <cheolsoo.kim@company.com>
 31  ์ž„๊บฝ์ • <kj.lim@company.com>


 

์•ˆ๋…•ํ•˜์„ธ์š” ์˜ค๋žœ๋งŒ์— ํฌ์ŠคํŒ…์„ ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

ํ•œ๋™์•ˆ ๋…ธ๊ฐ€๋‹ค์„ฑ ์ž‘์—…๋งŒ ํ•˜๋‹ค๋ณด๋‹ˆ ๋”ฑํžˆ ํฌ์ŠคํŒ…ํ•  ๋งŒํ•œ ๋‚ด์šฉ์ด ์—†์—ˆ๋„ค์š”.

 

์˜ค๋Š˜์€ AWS Elasticsearch(์ดํ•˜ ES)์˜ ์ธ๋ฑ์Šค๋ฅผ ์ž๋™์œผ๋กœ ์‚ญ์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ๋ ค๋“œ๋ฆฝ๋‹ˆ๋‹ค.

 

์ €๋Š” AWS ES๋ฅผ ๋กœ๊น…์„ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋กœ๊ทธ ๊ฒ€์ƒ‰ ์†๋„๊ฐ€ ๋นจ๋ผ ๋””๋ฒ„๊น…์‹œ ์›์ธํŒŒ์•…์„ ๋น ๋ฅด๊ฒŒ ํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ข‹์ฃ . ํŠน์ • ํŠธ๋ž˜ํ‚น ID๋กœ ํŠน์ • ํ”„๋กœ์„ธ์Šค์˜ ๋กœ๊ทธ๋“ค์„ ํ•˜๋‚˜๋กœ ๋ฌถ์–ด์ฃผ๋ฉด ํ•ด๋‹น ํ”„๋กœ์„ธ์Šค์˜ ๋กœ๊ทธ๋งŒ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•˜์—ฌ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

์•„๋ฌดํŠผ, ์ด๋ ‡๊ฒŒ ๋กœ๊ทธ๋ฅผ ์Œ“๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ES๊ฐ€ ์šฉ๋Ÿ‰์ด ๋ถ€์กฑํ•ด์ง€๋ฉด ๋ณ„๋‹ค๋ฅธ ์—๋Ÿฌ๋‚˜ ์•Œ๋žŒ์„ ์ฃผ์ง€์•Š๊ณ  ๋”์ด์ƒ ๋กœ๊ทธ๊ฐ€ ์Œ“์ด์ง€ ์•Š๊ฒŒ๋˜๋Š” ํ˜„์ƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๊ทธ๋Ÿฐ ์ผ์ด ์—†๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ๋ ˆ์ดํ„ฐ ์„ค์ •์„ ํ•˜์—ฌ ์‚ฌ์šฉ์ค‘์ž…๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ ์ธํ”„๋ผ ์ „๋ฌธํŒ€๊ณผ ๋…ผ์˜ํ•˜์—ฌ ์„ค์ •์„ ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ๋ ˆ์ดํ„ฐ๋ฅผ ์ด์šฉํ•˜๋ ค๋ฉด ์ข€ ๊ท€์ฐฎ์€ ์ž‘์—…๋“ค์ด ์ˆ˜๋ฐ˜๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ๋Š” ํ๋ ˆ์ดํ„ฐ ์„ค์ • ์—†์ด ์ˆ˜๋™์œผ๋กœ ๊ฐ€๋”์”ฉ ์ธ๋ฑ์Šค๋ฅผ ์‚ญ์ œํ•ด์ฃผ๊ณค ํ–ˆ์ฃ . ES ๋ฒ„์ „์ด ์˜ฌ๋ผ๊ฐ€๋ฉด์„œ ์ด์ œ๋Š” ํ‚ค๋ฐ”๋‚˜์—์„œ ๊ฐ„๋‹จํžˆ ์„ค์ •ํ•ด์ฃผ๋ฉด ์ž๋™์œผ๋กœ ํŠน์ • ๊ธฐ๊ฐ„์ด ์ง€๋‚œ ์ธ๋ฑ์Šค๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๊ฒŒ๋” ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ์˜ค๋ฆฌ์ง€๋‚  ES์—์„œ๋Š” ์ด ๊ธฐ๋Šฅ์„ ILM (Index Lifecycle Management) ๊ธฐ๋Šฅ์ด๋ผ๊ณ  ๋ถ€๋ฅด๋Š”๋ฐ AWS์—์„œ๋Š” ISM (Index State Management)๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

 

์šฐ์„  ์ œ๊ฐ€ ์‚ฌ์šฉ์ค‘์ธ AWS ES ๋ฒ„์ „์€ 7.9 ์ž…๋‹ˆ๋‹ค. ๋™์ผํ•œ 7.9 ๋ฒ„์ „์ด๋ผ๋„ ํŒจ์น˜๋ฒ„์ „์— ๋”ฐ๋ผ ํ‚ค๋ฐ”๋‚˜ ๋ฉ”๋‰ด๊ฐ€ ์กฐ๊ธˆ ์ƒ์ดํ•ฉ๋‹ˆ๋‹ค.

์ƒ์ดํ•œ ๋ฉ”๋‰ด๋Š” ๊ฐ๊ฐ ์Šค์ƒท์ฒจ๋ถ€๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์ด์ œ๋ถ€ํ„ฐ ์šฐ๋ฆฌ๊ฐ€ ํ•  ๊ฒƒ์€ 30์ผ์ด ์ง€๋‚œ ์ธ๋ฑ์Šค๋ฅผ ์ž๋™์œผ๋กœ ์‚ญ์ œํ•˜๋Š” ์„ค์ •์„ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ž‘์—…์€ 3์Šคํ…์œผ๋กœ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  1. ISM ์ •์ฑ… ์ƒ์„ฑ
  2. ๊ธฐ์กด ์ธ๋ฑ์Šค๋“ค์— ์ƒ์„ฑํ•œ ์ •์ฑ… ์ ์šฉ
  3. ์‹ ๊ทœ ์ธ๋ฑ์Šค๋“ค์— ์ •์ฑ… ์ ์šฉ

 

์šฐ์„  ํ‚ค๋ฐ”๋‚˜์— ์ ‘์†ํ•ด์„œ ๋ฉ”๋‰ด๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ตฌ ๋ฒ„์ „(์ขŒ) ๋ฉ”๋‰ด์™€ ์ตœ์‹  ๋ฒ„์ „(์šฐ) ๋ฉ”๋‰ด

 ์ขŒ์ธก์€ ํŒจ์น˜๋ฒ„์ „์ด ๊ตฌ๋ฒ„์ „์ธ ์ผ€์ด์Šค์ด๊ณ , ์šฐ์ธก์ด ์ตœ์‹ ๋ฒ„์ „ (๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ์ผ ๊ธฐ์ค€ R20210331) ํŒจ์น˜๋ฅผ ์ ์šฉํ•œ ํ‚ค๋ฐ”๋‚˜ ๋ฉ”๋‰ด์ž…๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ Index Management ๋ฉ”๋‰ด๋ฅผ ์„ ํƒํ•˜์—ฌ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ๋˜ ๋ฉ”๋‰ด๊ฐ€ ์•„๋ž˜์ฒ˜๋Ÿผ ๋‹ค๋ฅด๊ฒŒ ๋‚˜์˜ต๋‹ˆ๋‹ค.

๊ตฌ ๋ฒ„์ „(์ขŒ) ๋ฉ”๋‰ด์™€ ์ตœ์‹  ๋ฒ„์ „(์šฐ) ๋ฉ”๋‰ด

์ขŒ์ธก ์ฒ˜๋Ÿผ ๋ฉ”๋‰ด๊ฐ€ ๋‚˜์˜ฌ ๊ฒฝ์šฐ Index Policies ๋ฉ”๋‰ด๋ฅผ ์„ ํƒ, ์šฐ์ธก ์ฒ˜๋Ÿผ ๋ฉ”๋‰ด๊ฐ€ ๋‚˜์˜ฌ ๊ฒฝ์šฐ State management policies ๋ฉ”๋‰ด๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

 

๊ทธ ๋‹ค์Œ๋ถ€ํ„ฐ๋Š” ๊ฑฐ์˜ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ ๋ฒ„์ „ ์Šค์ƒท
์‹  ๋ฒ„์ „ ์Šค์ƒท

์ œ๋ชฉ๋งŒ ๋‹ค๋ฅผ ๋ฟ ๋‚ด์šฉ์€ ๋ชจ๋‘ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ create policy๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

 

Name policy ์—๋Š” ์›ํ•˜๋Š” ์ด๋ฆ„์„ ๋ช…๋ช…ํ•ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ €๋Š” 30์ผ์ด ์ง€๋‚œ ์ธ๋ฑ์Šค๋ฅผ ์‚ญ์ œํ•œ๋‹ค๋Š” ์˜๋ฏธ๋กœ delete_old_indexes_30d๋ผ๊ณ  ๋ช…๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  define policy ์—๋Š” ์•„๋ž˜ json ํฌ๋ฉง ๋ฐ์ดํ„ฐ๋ฅผ ์ ์–ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

{
    "policy": {
        "policy_id": "delete_old_indexes_30d",
        "description": "delete old indexes",
        "default_state": "hot",
        "states": [
            {
                "name": "hot",
                "actions": [],
                "transitions": [
                    {
                        "state_name": "delete",
                        "conditions": {
                            "min_index_age": "30d"
                        }
                    }
                ]
            },
            {
                "name": "delete",
                "actions": [
                    {
                        "delete": {}
                    }
                ],
                "transitions": []
            }
        ]
    }
}

์ง€๊ธˆ ์šฐ๋ฆฌ๋Š” ์ธ๋ฑ์Šค ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์„ค์ •์„ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ policy ๋‚ด์— ์ƒํƒœ์— ๋Œ€ํ•œ ์ •์˜๋ฅผ ํ•ด์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

policy_id๋Š” Name policy์— ๋ช…๋ช…ํ•ด์ฃผ์—ˆ๋˜ ์ด๋ฆ„์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ณ , default_state ๋ฅผ hot ์ด๋ผ๋Š” ์ƒํƒœ๋กœ ์„ค์ •ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๋Š” ๋‘ ๊ฐ€์ง€ ์ƒํƒœ๋ฅผ ์ •์˜ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. hot ๊ณผ delete.

์ธ๋ฑ์Šค์˜ ๊ธฐ๋ณธ์ƒํƒœ๋กœ๋Š” hot์„ ์ •์˜๋ฅผ ํ•ด์ฃผ์—ˆ๊ณ  ๊ทธ ์ƒํƒœ์˜ ์ธ๋ฑ์Šค๋“ค์€ transitions์— ์„ค์ •๋˜์–ด์žˆ๋Š” ์กฐ๊ฑด์— ๋”ฐ๋ผ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰ max_index_age๊ฐ€ 30d (30์ผ)์ด ์ง€๋‚˜๋ฉด ์ƒํƒœ๋ฅผ delete๋กœ ๋ณ€๊ฒฝ(transition)ํ•˜๊ฒŒ ๋˜์ฃ . ๊ทธ๋ ‡๊ฒŒ delete ์ƒํƒœ๊ฐ€ ๋œ ์ธ๋ฑ์Šค๋“ค์€ delete ์ƒํƒœ ์ •์˜์˜ actions์— ์žˆ๋Š” delete ์„ค์ •์œผ๋กœ ์ธํ•ด ์‚ญ์ œ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

์ด์ œ ISM ์ •์ฑ… ์ƒ์„ฑ์„ ์™„๋ฃŒํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ •์ฑ…์„ ์ƒ์„ฑํ•œ ๋’ค์—๋Š” ๊ธฐ์กด ์ธ๋ฑ์Šค๋“ค์— ๋Œ€ํ•ด์„œ ํ•ด๋‹น ์ •์ฑ…์„ ์ ์šฉํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ •์ฑ…์„ ์ €์žฅํ•œ ๋’ค์— ์ขŒ์ธก ๋ฉ”๋‰ด์—์„œ Indices ๋กœ ๋“ค์–ด๊ฐ€๋ณด๋ฉด ํ˜„์žฌ ์ธ๋ฑ์Šค๋“ค ๋ชฉ๋ก์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

์ธ๋ฑ์Šค ๋ชฉ๋ก

๋ชฉ๋ก์—์„œ ํ•˜๋‚˜ ์ด์ƒ์˜ ์ธ๋ฑ์Šค๋ฅผ ์„ ํƒํ•˜๋ฉด ์šฐ์ƒ๋‹จ์˜ Apply policy ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™” ๋ฉ๋‹ˆ๋‹ค. ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ •์ฑ…์ด ๋ฐ˜์˜๋˜๊ณ  ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•ด๋ณด์‹œ๋ฉด Managed by Policy ๊ฐ’์ด No ์—์„œ Yes๋กœ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. .kibana ์ธ๋ฑ์Šค์ฒ˜๋Ÿผ ์ž˜ ๋ชจ๋ฅด๋ฉด ๊ฑด๋“ค์ง€ ๋ง์•„์•ผํ•˜๋Š” ์ธ๋ฑ์Šค๋“ค๋„ ์—ฌ๊ธฐ ๋ชฉ๋ก์— ์กฐํšŒ๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด ์„ ํƒํ•˜์—ฌ ์ •์ฑ…์„ ์ ์šฉ์‹œํ‚ฌ ๋•Œ์—๋Š” ๊ทธ๋Ÿฐ ์ธ๋ฑ์Šค๋“ค์ด ํฌํ•จ๋˜์–ด์žˆ์ง€ ์•Š์€์ง€ ์ž˜ ํ™•์ธํ•˜์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ž˜๋ชป ์ ์šฉํ–ˆ๋‹ค๋ฉด ์ ์šฉ ์ทจ์†Œ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ •์ฑ…์„ ์ ์šฉํ•œ ์ธ๋ฑ์Šค๋“ค์„ ์—ฌ๊ธฐ์„œ๋Š” Managed Index๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ ์šฉ์„ ์ทจ์†Œํ•˜๋ ค๊ณ  ํ•  ๋•Œ์—๋Š” Managed Indices ๋˜๋Š” Policy managed indices ๋ฉ”๋‰ด๋กœ ๋“ค์–ด๊ฐ€์„œ ์ธ๋ฑ์Šค๋ฅผ ์กฐํšŒํ•˜์—ฌ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ ๋ฒ„์ „ ํ™”๋ฉด
์ตœ์‹  ๋ฒ„์ „ ํ™”๋ฉด (R20210331)

์ธ๋ฑ์Šค๋ฅผ ์„ ํƒํ•˜๊ณ  Remove policy(์‚ญ์ œ) ํ•˜๊ฑฐ๋‚˜ Change policy(๋ณ€๊ฒฝ)์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

์ด์ƒ์œผ๋กœ AWS ES์—์„œ ISM์„ ํ™œ์šฉํ•˜์—ฌ ์ธ๋ฑ์Šค ์ž๋™์‚ญ์ œ ์ •์ฑ…์„ ์ƒ์„ฑ ๋ฐ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

 

 

๊ถ๊ธˆํ•œ ์ ์ด๋‚˜ ์ž˜๋ชป๋œ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“ ์ง€ ๋Œ“๊ธ€๋‹ฌ์•„์ฃผ์„ธ์š”~

 

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์Šคํ”„๋ง๋ถ€ํŠธ 1 ์„ ์‚ฌ์šฉํ•˜๋˜ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์•ฑ์„ MSA ์ ์šฉ ์ž‘์—…์„ ํ†ตํ•ด ์—ฌ๋Ÿฌ ์•ฑ์œผ๋กœ ๋ถ„๋ฆฌํ•˜๋ฉด์„œ ์Šคํ”„๋ง๋ถ€ํŠธ 2๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ณผ์ •์—์„œ ๋‹ค๋ฅธ ํšŒ์‚ฌ์™€ API ์—ฐ๋™ํ•˜๊ณ  ์žˆ๋˜ ๋ถ€๋ถ„์—์„œ ์ธ์ฝ”๋”ฉ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

์†Œ์Šค ์ด์ „์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋นผ๋จน์€ ๋ถ€๋ถ„์ด ์žˆ์–ด์„œ์˜€์ฃ .

์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•œ ํšŒ์‚ฌ์ธก์—์„œ ์›์ธ์œผ๋กœ ์–˜๊ธฐํ•œ ๊ฒƒ์€ ์‘๋‹ต ํ—ค๋”์˜ Content-Type์— utf-8 ์ธ์ฝ”๋”ฉ ์„ค์ •์ด ๋น ์ ธ์žˆ๋‹ค๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ํ™•์ธํ•ด๋ณด๋‹ˆ ์Šคํ”„๋ง๋ถ€ํŠธ 1 ๊ธฐ๋ฐ˜ ์•ฑ์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์„ค์ •์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

	@Bean
	public HttpMessageConverter<String> responseBodyConverter() {
		return new StringHttpMessageConverter(Charset.forName("UTF-8"));
	}

	@Bean
	public Filter characterEncodingFilter() {
		CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
		characterEncodingFilter.setEncoding("UTF-8");
		characterEncodingFilter.setForceEncoding(true);
		return characterEncodingFilter;
	}

๊ทธ๋ฆฌ๊ณ  ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ์‘๋‹ตํ—ค๋”์˜ Content-Type์— application/json;charset=utf-8 ์ด๋ผ๊ณ  ๋˜์–ด์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ ๊ทœ ์•ฑ์—์„œ๋Š” charset ์ •๋ณด๊ฐ€ ๋น ์ ธ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ์œ„ ์„ค์ •์„ ๊ทธ๋Œ€๋กœ ์Šคํ”„๋ง๋ถ€ํŠธ 2 ๊ธฐ๋ฐ˜์˜ ์‹ ๊ทœ๋กœ ๊ฐœ๋ฐœํ•œ ์•ฑ์ชฝ์— ๋„ฃ์œผ๋ ค ํ–ˆ๋”๋‹ˆ ์•ฑ ๊ธฐ๋™ ์‹œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'characterEncodingFilter', defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/HttpEncodingAutoConfiguration.class], could not be registered. A bean with that name has already been defined in com.yanolja.affiliate.out.api.AffiliateOutApiApplication and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

์ด๋ฏธ ๋™์ผํ•œ ์ด๋ฆ„์˜ ๋นˆ์ด ์กด์žฌํ•˜๋‹ˆ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๋ผ๋Š” ์–˜๊ธฐ์˜€์ฃ .

๊ทผ๋ฐ ์ƒ๊ฐํ•ด๋ณด๋‹ˆ ๊ตณ์ด ์ด๋ ‡๊ฒŒ ํ•ด์•ผํ•˜๋‚˜ ๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์–ด ์ข€ ์ฐพ์•„๋ณด๋‹ˆ,

์ž๋ฐ” ์†Œ์Šค์— ์ €๋ ‡๊ฒŒ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ์Šคํ”„๋ง ์„ค์ • ๋งŒ์œผ๋กœ๋„ ํ—ค๋”์˜ ์ธ์ฝ”๋”ฉ ์„ค์ •์€ ๊ฐ€๋Šฅํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฐ”๋กœ server.servlet.encoding.charset ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. 

ํ•˜์œ„ ๋ฒ„์ „์—์„œ๋Š” ์ด ์„ค์ •์ด spring.http.encoding.charset ์œผ๋กœ ์‚ฌ์šฉ๋˜์—ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

์ž๋ฐ” ์†Œ์Šค์—์„œ characterEncodingFilter ๋นˆ ์ƒ์„ฑ๋กœ์ง์„ ์ œ๊ฑฐํ•˜๊ณ , application.yml ํŒŒ์ผ์— ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

server:
  servlet:
    encoding:
      charset: UTF-8

 

๊ทธ๋ฆฌ๊ณ  ์•ฑ์„ ์žฌ๋ถ€ํŒ… ํ•ด์ฃผ๋ฉด ์‘๋‹ตํ—ค๋”์˜ Content-Type ๊ฐ’์ด application/json;charset=utf-8 ์œผ๋กœ ๋ณ€๊ฒฝ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๊ฐ’์ด ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค๋ฉด server.servlet.encoding.force-response ๋˜๋Š” server.servlet.encoding.force ์„ค์ •๊ฐ’์„ true๋กœ ์„ค์ •ํ•ด์„œ ๋‹ค์‹œ ํ•ด๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 

 

์ฐธ๊ณ ๋ฌธ์„œ: docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#server.servlet.encoding.charset 

์ž๋ฐ”์—์„œ ๋น„๋™๊ธฐ ํ˜ธ์ถœํ•˜๊ธฐ (AsyncRestTemplate, ListenableFuture)

์•ˆ๋…•ํ•˜์„ธ์š”, ์ตœ๊ทผ์— ๊ด€๋ฆฌ์ž์šฉ ์•ฑ์„ ๊ฐœ์„ ํ•˜๋‹ค๊ฐ€ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด์„œ ๊ณต์œ ํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์ด ์žˆ์–ด ํฌ์ŠคํŒ…์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ œ๊ฐ€ ๊ด€๋ฆฌ์ค‘์ธ ๊ด€๋ฆฌ์ž์šฉ ์•ฑ์€ Spring Framework 4, Java 8, Bootstrap 3 ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

์˜ค๋ž˜๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์—…๊ทธ๋ ˆ์ด๋“œ ํ•˜๊ณ  ์‹ถ์ง€๋งŒ ๋ฉ”์ธ ์—…๋ฌด๊ฐ€ ์•„๋‹ˆ๋‹ค๋ณด๋‹ˆ ์งฌ๋‚ ๋•Œ ์กฐ๊ธˆ์”ฉ ์—…๊ทธ๋ ˆ์ด๋“œ ํ•ด์ฃผ๊ณ  ์žˆ๋Š”๋ฐ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์ด๋‚˜ ์Šคํ”„๋ง ๋ฒ„์ „์„ ์˜ฌ๋ฆฌ๊ธฐ์—” ์ž‘์—…๋Ÿ‰์ด ๋งŽ์•„๋ณด์—ฌ์„œ ์•ˆ๊ฑด๋“œ๋ฆฌ๊ณ  ์žˆ๋„ค์š”.

 

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

 

์ฒซ ๋ฒˆ์งธ๋Š” callee ๋ฉ”์„œ๋“œ ์ชฝ์—์„œ @Async ๋ฅผ ๋ถ™์—ฌ์„œ ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๊ณ ์š”

์ด ๋ฐฉ๋ฒ•์€ callee๊ฐ€ ์™ธ๋ถ€ ํŒ€์ด๊ฑฐ๋‚˜ ์™ธ๋ถ€ ํšŒ์‚ฌ์ผ ๊ฒฝ์šฐ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฑฐ๋‚˜ ์ผ์ด ๋นจ๋ฆฌ ์ง„ํ–‰ํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตณ์ด ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์žˆ์ง€ ์•Š์€์ด์ƒ callee์ชฝ์— ์š”์ฒญ์„ ํ•  ํ•„์š”๋Š” ์ „ํ˜€ ์—†์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ callee๊ฐ€ ๋‚ด๋ถ€์— ์žˆ๋‹ค๋ฉด ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์กฐ์ƒ ์ด ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋‘ ๋ฒˆ์งธ๋Š” caller๊ฐ€ ํ˜ธ์ถœ๋ฐฉ์‹์„ ์ง์ ‘ ๋น„๋™๊ธฐ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

callee๊ฐ€ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋˜์ง€ caller๋Š” ์‹ ๊ฒฝ์“ธ ํ•„์š”๊ฐ€ ์—†์ฃ . 

๋‹จ์ง€ ์‘๋‹ต๋งŒ ์ž˜ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

์˜ค๋Š˜ ์†Œ๊ฐœํ•ด ๋“œ๋ฆด ๋‚ด์šฉ์€ ๋‘ ๋ฒˆ์งธ ๋ฐฉ์‹์ธ caller ์ž…์žฅ์—์„œ ๋น„๋™๊ธฐ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

์Šคํ”„๋ง์„ ์‚ฌ์šฉํ•˜์‹ ๋‹ค๋ฉด API ํ˜ธ์ถœํ•˜์‹ค ๋•Œ RestTemplate์„ ์‚ฌ์šฉํ•˜์‹ค ๊ฒ๋‹ˆ๋‹ค.

 

๊ทผ๋ฐ ์Šคํ”„๋ง์—์„œ๋Š” ๋น„๋™๊ธฐ ํ˜ธ์ถœ์„ ์œ„ํ•ด์„œ ์ด๋ฏธ AsyncRestTemplate ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

AsyncRestTemplate ์„ ์ด์šฉํ•ด์„œ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์€ RestTemplate๊ณผ ๋‹ค๋ฅผ๊ฒŒ ์—†์ฃ .

๊ทธ๋ฆฌ๊ณ  ๋‹น์—ฐํžˆ๋„ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋‹ค์Œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ทธ ์‘๋‹ต์€ ListenableFuture<ResponseEntity<T>> ํƒ€์ž…์œผ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ํ•œ๋ฒˆ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

    // Autowired๋กœ ์ฃผ์ž…๋œ ์ƒํƒœ๋ผ ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.
    private final AsyncRestTemplate asyncRestTemplate;
	
    public void test() {
        URI uri = UriComponentsBuilder.fromUriString("http://localhost")
                .path("/test")
                .build()
                .toUri();

        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");

        HttpEntity<String> requestEntity = new HttpEntity<>(headers);
        HttpMethod httpMethod = HttpMethod.POST;

        ListenableFuture<ResponseEntity<Void>> res = 
        	asyncRestTemplate.exchange(uri, httpMethod, requestEntity, Void.class);
        log.info("ํ˜ธ์ถœ ์„ฑ๊ณต!!");
    }

์œ„ ์ฝ”๋“œ๋Š” http://localhost/test ๋ฅผ POST ๋ฐฉ์‹์œผ๋กœ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ—ค๋”์— application/json;charset=UTF-8 ์ •๋ณด๋„ ๋„ฃ์–ด์ฃผ๊ณ  ์žˆ๊ณ ์š” ์‘๋‹ต๋ฉ”์‹œ์ง€์— ๋Œ€ํ•ด์„œ๋Š” Void๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ๋ฉ”์‹œ์ง€๋ฅผ ์‘๋‹ต์œผ๋กœ ์ฃผ๋˜์ง€ ์‹ ๊ฒฝ์•ˆ์“ด๋‹ค๋Š” ๊ฑฐ๊ฒ ์ฃ .

๊ทธ๋ฆฌ๊ณ  ํ˜ธ์ถœ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๋ฅผ ListenableFuture<ResponseEntity<Void>> res ์— ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์‘๋‹ต์„ ๋ฐ›๊ธฐ ์ „์— ํ˜ธ์ถœํ•˜์ž๋งˆ์ž ํ˜ธ์ถœ ์„ฑ๊ณต!! ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฌ๋ฉด ๊ทธ๋ ‡๊ฒŒ ๋ฐ›์€ ์‘๋‹ต์˜ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ์ •์ƒ๊ณผ ์‹คํŒจ๋ฅผ ๊ตฌ๋ถ„ํ•ด์„œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค˜์•ผ ํ• ํ…๋ฐ ๊ทธ๊ฑด ์–ด๋–ป๊ฒŒ ํ• ๊นŒ์š”?

res ์— callback ํ•จ์ˆ˜๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ฝœ๋ฐฑ์„ ์ถ”๊ฐ€ํ•œ ์˜ˆ์ œ๋ฅผ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

    // Autowired๋กœ ์ฃผ์ž…๋œ ์ƒํƒœ๋ผ ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.
    private final AsyncRestTemplate asyncRestTemplate;

    public void test() {
        URI uri = UriComponentsBuilder.fromUriString("http://localhost")
                .path("/test")
                .build()
                .toUri();

        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");

        HttpEntity<String> requestEntity = new HttpEntity<>(headers);
        HttpMethod httpMethod = HttpMethod.POST;

        ListenableFuture<ResponseEntity<Void>> res = asyncRestTemplate.exchange(uri, httpMethod, requestEntity, Void.class);
        res.addCallback(new ListenableFutureCallback<ResponseEntity<Void>>() {
            @Override
            public void onFailure(Throwable th) {
                log.error("์‹คํŒจ");
            }

            @Override
            public void onSuccess(ResponseEntity<Void> voidResponseEntity) {
                if (HttpUtil.isNot2xxSuccessful(voidResponseEntity)) {
                    log.error("์‹คํŒจ, ์‘๋‹ต์ฝ”๋“œ:{}", voidResponseEntity.getStatusCodeValue();
                } else {
                    log.info("์„ฑ๊ณต");
                    // TODO: ์ถ”๊ฐ€ ์ž‘์—…
                }
            }
        });

    }

์—ฌ๊ธฐ์„œ๋Š” annonymous ListenableFutureCallback ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ addCallback ๋ฉ”์„œ๋“œ์— ๋„˜๊ฒจ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

onFailure ์™€ onSuccess ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์ฃผ๋ฉด ๋˜๋Š”๋ฐ์š” ์ด๋•Œ ์ฃผ์˜ํ•  ์ ์ด ํ•˜๋‚˜ ์žˆ์Šต๋‹ˆ๋‹ค.

onSuccess ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ์ผ€์ด์Šค๊ฐ€ 2xx ์„ฑ๊ณต์ธ ์ผ€์ด์Šค๋งŒ์ด ์•„๋‹ˆ๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค.

4xx ์ผ€์ด์Šค๋„ onSuccess ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

onFailure๋Š” SocketTimeout๊ณผ ๊ฐ™์€ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์‹ค์งˆ์ ์œผ๋กœ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ์„ฑ๊ณต ์€ response entity์—์„œ ์‘๋‹ต์ฝ”๋“œ๋กœ ํ™•์ธํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ฐธ๊ณ ๋กœ ์œ„ ์ฝ”๋“œ์—์„œ HttpUtil.isNot2xxSuccessful(voidResponseEntity) ๋ฉ”์„œ๋“œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์ฝ”๋”ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

public class HttpUtil {

    public static boolean is2xxSuccessful(ResponseEntity responseEntity) {
        return HttpStatus.Series.valueOf(responseEntity.getStatusCodeValue())
        	.equals(HttpStatus.Series.SUCCESSFUL);
    }

    public static boolean isNot2xxSuccessful(ResponseEntity responseEntity) {
        return !is2xxSuccessful(responseEntity);
    }
}

ListenableFuture<ResponseEntity<T>> ์—์„œ ์ œ๊ณตํ•˜๋Š” addCallback ๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” ์•„๋ž˜ ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

 

void addCallback(ListenableFutureCallback<? super T> var1);
void addCallback(SuccessCallback<? super T> var1, FailureCallback var2);

 

์œ„ ์˜ˆ์ œ์—์„œ๋Š” ์ฒซ ๋ฒˆ์งธ addCallback ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค๋งŒ, ๋งŒ์•ฝ SuccessCallback์™€ FailureCallback์„ ๋‚˜๋ˆ ์„œ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๊ฐ๊ฐ ๊ตฌํ˜„ํ•ด์„œ addCallback ๋ฉ”์„œ๋“œ์— ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•ด์ฃผ์…”๋„ ๋ฉ๋‹ˆ๋‹ค.

 

์ถ”๊ฐ€๋กœ AsyncRestTemplate ์„ ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” ๊ธฐ๋ณธ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€๋งŒ ํƒ€์ž„์•„์›ƒ ๋“ฑ ์„ค์ •์„ ๋ฐ”๊ฟ”์„œ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค๋ฉด ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ๋„์›€์ด ๋˜์‹ค๊ฒ๋‹ˆ๋‹ค.

    @Bean
    public AsyncRestTemplate asyncRestTemplate2() {
        SimpleAsyncTaskExecutor simpleAsyncTaskExecutor = new SimpleAsyncTaskExecutor();
        simpleAsyncTaskExecutor.setTaskDecorator(new MdcTaskDecorator());

        SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        simpleClientHttpRequestFactory.setConnectTimeout(30_000);    // ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„
        simpleClientHttpRequestFactory.setReadTimeout(30_000);       // ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„
        simpleClientHttpRequestFactory.setTaskExecutor(simpleAsyncTaskExecutor);

        AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate(simpleClientHttpRequestFactory);

        return asyncRestTemplate;
    }

    static class MdcTaskDecorator implements TaskDecorator {

        @Override
        public Runnable decorate(Runnable runnable) {
            // Web thread context
            Map<String, String> contextMap = MDC.getCopyOfContextMap();
            return () -> {
                try {
                    // @Async thread context
                    MDC.setContextMap(contextMap);
                    runnable.run();
                } finally {
                    MDC.clear();
                }
            };
        }
    }

 

์ด์ƒ์œผ๋กœ AsyncRestTemplate์„ ์ด์šฉํ•˜์—ฌ ์ž๋ฐ”์—์„œ ๋น„๋™๊ธฐ ํ˜ธ์ถœํ•˜๊ธฐ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ดค์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ AsyncRestTemplate์€ Spring 5๋กœ ๋„˜์–ด๊ฐ€๋ฉด์„œ deprecated ๋˜์—ˆ๊ณ , WebClient๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Deprecated as of Spring 5.0, in favor of org.springframework.web.reactive.function.client.WebClient

WebClient์— ๋Œ€ํ•ด์„œ๋Š” ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ ๋‹ค๋ฃจ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์˜ค๋Š˜๋„ ์ฆํ”„ํ–‰ํ”„ํ•˜์„ธ์š”~

 

 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” SpringBoot์—์„œ ThreadPoolTaskExecutor๋ฅผ ์–ด๋–ป๊ฒŒ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค์ •ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ๋ ค๋“œ๋ฆฝ๋‹ˆ๋‹ค.

SpringBoot ํ”„๋กœ์ ํŠธ๋Š” ์ด๋ฏธ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค๋Š” ์ „์ œํ•˜์— Java11, SpringBoot 2.4.2 ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ThreadPoolTaskExecutor์˜ ์„ค์ • ๋ฐ ๊ตฌ์ฒด์ ์ธ ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด์„œ๋Š” ์ด์ „์— ํฌ์ŠคํŒ…ํ•œ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 

ThreadPoolTaskExecutor์˜ ์‚ฌ์šฉ๋ฒ•

 

ThreadPoolTaskExecutor๋ฅผ ์ด์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌํ•˜๊ธฐ(๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ)

async(๋น„๋™๊ธฐ) ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ThreadPoolTaskExecutor ThreadPoolTaskExecutor๋ฅผ ์ด์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ThreadPoolTaskExecutor๋Š” ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” ํด๋ž˜์Šค๋กœ org.springframework.s..

keichee.tistory.com

ThreadPoolTaskExecutor Bean ๋“ฑ๋กํ•˜๊ธฐ

์šฐ์„  ThreadPoolTaskExecutor๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. 

TaskExecutorConfig ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ณ  @Configuration ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์„ค์ •์„ ์œ„ํ•œ ํด๋ž˜์Šค๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
public class TaskExecutorConfig {

	@Bean(name = "executor")
	public ThreadPoolTaskExecutor executor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setThreadNamePrefix("my-");
		return executor;
	}
}

๊ทธ๋ฆฌ๊ณ  executor() ๋ฉ”์„œ๋“œ๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์„œ ThreadPoolTaskExecutor๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋„๋ก ํ•˜๊ณ  @Bean์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด์„œ name ์†์„ฑ์„ ์ง€์ •ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ name ์†์„ฑ์˜ ๊ฐ’์€ ๋ฉ”์„œ๋“œ๋ช…๊ณผ ๋‹ฌ๋ผ๊ณ  ๋ฌด๊ด€ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ํ•ด๋‹น executor๋ฅผ ์ด์šฉํ•˜์—ฌ ์“ฐ๋ ˆ๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผฐ์„ ๋•Œ ๋‚ด๊ฐ€ ๋งŒ๋“  ThreadPoolTaskExecutor๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ์“ฐ๋ ˆ๋“œ๋ช…prefix๋ฅผ "my-"๋กœ ์„ธํŒ…ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. 

์ด์ „ ํฌ์ŠคํŒ…์„ ์ฝ์–ด๋ณด์…จ๊ฑฐ๋‚˜ ๋˜๋Š” ThreadPoolTaskExecutor๋ฅผ ์ง์—… new ํ‚ค์›Œ๋“œ๋กœ ์ƒ์„ฑํ•ด์„œ ์‚ฌ์šฉํ•ด๋ณด์‹  ๋ถ„์€ ๋ˆˆ์น˜์ฑ„์…จ์„ ์ˆ˜๋„ ์žˆ์„ํ…๋ฐ์š”, ์—ฌ๊ธฐ์„œ initialize()๋ฅผ ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ณต์‹๋ฌธ์„œ์—๋Š” initialize ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜์™€์žˆ์ง€๋งŒ ๊ตณ์ด ํ•˜์ง€ ์•Š์•„๋„ ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋  ๋•Œ initialize๋ฅผ ํ•˜๋”๊ตฐ์š”.

 

@Autowired ๋กœ ThreadPoolTaskExecutor ์‚ฌ์šฉํ•˜๊ธฐ

์•„๋ž˜๋Š” ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ executor๋ฅผ ์„œ๋น„์Šค ๋ ˆ์ด์–ด์—์„œ autowiredํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class TestService {

  @Autowired
  @Qualifier("executor")
  private ThreadPoolTaskExecutor executor;

  public void executeThreads() {
      log.info("executing threads....");
      Runnable r = () -> {
          try {
              log.info(Thread.currentThread().getName() + ", Now sleeping 10 seconds...");
              Thread.sleep(10000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      };

      for (int i = 0; i < 10; i++) {
          executor.execute(r);
      }
  }
}

TestService๋ผ๋Š” ์„œ๋น„์Šค ํด๋ž˜์Šค์—์„œ ๋ฉค๋ฒ„๋ณ€์ˆ˜ executor์— ์œ„์—์„œ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ ๊ฐ์ฒด๋ฅผ ์“ฐ๋„๋ก @Qualifier๋กœ executor ์ด๋ฆ„์„ ๋ช…์‹œํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์“ฐ๋ ˆ๋“œ ๋ช…์ด my-1 ๋กœ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. corePoolSize๋ฅผ default ๊ฐ’์ธ 1 ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์‹ฑ๊ธ€ ์“ฐ๋ ˆ๋“œ๋กœ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— my-2, my-3์€ ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. corePoolSize ๋ฅผ 2 ์ด์ƒ์œผ๋กœ ์„ธํŒ…ํ•ด์ฃผ๋ฉด my-2, my-3 ๋“ฑ์˜ ์“ฐ๋ ˆ๋“œ๋„ ํ™•์ธ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

2021-01-26 18:16:23.147  INFO 16390 --- [           my-1] com.keichee.test.service.TestService     : my-1, Now sleeping 10 seconds...
2021-01-26 18:16:33.150  INFO 16390 --- [           my-1] com.keichee.test.service.TestService     : my-1, Now sleeping 10 seconds...
2021-01-26 18:16:43.152  INFO 16390 --- [           my-1] com.keichee.test.service.TestService     : my-1, Now sleeping 10 seconds...
2021-01-26 18:16:53.157  INFO 16390 --- [           my-1] com.keichee.test.service.TestService     : my-1, Now sleeping 10 seconds...

 

@Async๋กœ ThreadPoolTaskExecutor ์‚ฌ์šฉํ•˜๊ธฐ

@Async ์–ด๋…ธํ…Œ์ด์…˜์„ ์ด์šฉํ•˜์—ฌ ์œ„์—์„œ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ ThreadPoolTaskExecutor๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด @EnableAsync ๋„ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ TaskExecutorConfig ํด๋ž˜์Šค์— @EnableAsync ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@EnableAsync
@Configuration
public class TaskExecutorConfig {

	@Bean(name = "executor")
	public ThreadPoolTaskExecutor executor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setThreadNamePrefix("my-");
		return executor;
	}
}

 

๊ทธ๋ฆฌ๊ณ  ์„œ๋น„์Šค ๋ ˆ์ด์–ด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ•˜๋‚˜ ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class TestService {

    @Async("executor")
    public void task() {
        try {
            log.info(Thread.currentThread().getName() + ", Now sleeping 10 seconds...");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

์ด์ œ controller ์ชฝ์—์„œ task() ๋ฉ”์„œ๋“œ๋ฅผ loop ๋Œ๋ฉด์„œ ํ˜ธ์ถœํ•ด๋ณด๋ฉด @Autowired ๋ฅผ ์ด์šฉํ•œ ๋ฐฉ๋ฒ•๊ณผ ๋™์ผํ•˜๊ฒŒ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค.

 

์ด์ƒ์œผ๋กœ SpringBoot์—์„œ ThreadPoolTaskExecutor ์„ค์ •ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๊ฐ„๋žตํ•˜๊ฒŒ ๋‹ค์‹œ ์ •๋ฆฌํ•ด๋ณด์ž๋ฉด

 

1. @Configuration ์œผ๋กœ ๋“ฑ๋กํ•œ ํด๋ž˜์Šค์— executor @Bean ์ถ”๊ฐ€ (@Async ๋ฅผ ์ด์šฉํ•  ๊ฒฝ์šฐ @EnableAsync ๋„ ์ถ”๊ฐ€)

2. @Autowired @Qualifier ๋กœ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๋˜๋Š” ๋ฉ”์„œ๋“œ ๋ ˆ๋ฒจ์— @Async ๋ฅผ ๋ถ™์—ฌ ์‚ฌ์šฉ

 

์ด๋ ‡๊ฒŒ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”.

 

๊ทธ๋Ÿผ ์˜ค๋Š˜๋„ ํ–‰๋ณตํ•œ ์ฝ”๋”ฉํ•˜์„ธ์š”~

ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•˜๋‹ค๋ณด๋ฉด DB ์กฐํšŒ ์‹œ์— ์ •๋ ฌํ•ด์„œ ์กฐํšŒํ•ด์˜ค๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ ์ •๋ ฌํ•ด์•ผ ํ•  ๋•Œ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ค๋Š˜์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐฐ์—ด, ๋ฆฌ์ŠคํŠธ, ๋งต, ์…‹์— ๋Œ€ํ•˜์—ฌ ๊ฐ๊ฐ ์˜ค๋ฆ„์ฐจ์ˆœ, ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

1. ๋ฐฐ์—ด ์ •๋ ฌํ•˜๊ธฐ

์šฐ์„  ์•„๋ž˜์™€ ๊ฐ™์ด int ๋ฐฐ์—ด์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

int[] arr = new int[] { 5, 1, 89, 255, 7, 88, 200, 123, 66 };

๋ฐฐ์—ด์˜ ์ •๋ ฌ์€ java.util.Arrays ํด๋ž˜์Šค์— ์žˆ๋Š” sort() ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ชจ๋“  primitive ํƒ€์ž…์— ๋Œ€ํ•œ ์ •๋ ฌ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์›์‹œํƒ€์ž…์ด ์•„๋‹Œ Object[] ๋„ ์ •๋ ฌ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

int[] arr = new int[] { 5, 1, 89, 255, 7, 88, 200, 123, 66 };
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));

Arrays.sort(๋ฐฐ์—ด) ํ˜ธ์ถœ์„ ํ•˜๋ฉด ์ธ์ž๋กœ ๋„˜๊ฒจ์ค€ ๋ฐฐ์—ด์„ ์ •๋ ฌํ•ด์ค๋‹ˆ๋‹ค. 

๊ทธ๋ฆฌ๊ณ  ์ถœ๋ ฅ์„ ํ•ด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž˜ ์ •๋ ฌ์ด ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

[1, 5, 7, 66, 88, 89, 123, 200, 255]

๊ทธ๋Ÿผ ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์–ด๋–ค ๊ฒƒ์ผ๊นŒ์š”?

Java8 ๋ฌธ์„œ์—๋Š” ๋“€์–ผ ํ”ผ๋ด‡ ํ€ต์†ŒํŠธ(Dual-Pivot Quick Sort) ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๋‚˜์™€์žˆ์Šต๋‹ˆ๋‹ค.

๋“€์–ผ ํ”ผ๋ด‡ ํ€ต์†ŒํŠธ๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์…‹์— ๋Œ€ํ•ด์„œ O(n log(n)) ์˜ ํผํฌ๋จผ์Šค๋ฅผ ๋ณด์ด๊ณ  ์ผ๋ฐ˜์ ์œผ๋กœ ๊ธฐ์กด์˜ One-Pivot ํ€ต์†ŒํŠธ ์ •๋ ฌ๋ณด๋‹ค ๋น ๋ฅด๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

Java7 ๋ฌธ์„œ, Java14 ๋ฌธ์„œ์—๋„ ๋™์ผํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜์™€์žˆ์Šต๋‹ˆ๋‹ค.

1.1 ๋ถ€๋ถ„์ •๋ ฌ

์œ„ ์˜ˆ์ œ๋Š” ๋ฐฐ์—ด์˜ ์ „์ฒด ์š”์†Œ๋ฅผ ์ •๋ ฌํ•˜๋Š” ๋ฐ˜๋ฉด ๋ฐฐ์—ด์˜ ์ผ๋ถ€๋ถ„๋งŒ ์ •๋ ฌ์„ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ๋ถ€๋ถ„๋งŒ ์ •๋ ฌํ•  ๋•Œ๋Š” Arrays.sort(int[] a, int fromIndex, int toIndex) ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

int[] arr = new int[] { 5, 1, 89, 255, 7, 88, 200, 123, 66 };
Arrays.sort(arr, 0, 4);
System.out.println(Arrays.toString(arr));

์ถœ๋ ฅ์„ ํ•ด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด 0๋ฒˆ ์ธ๋ฑ์Šค๋ถ€ํ„ฐ 3๋ฒˆ ์ธ๋ฑ์Šค๊นŒ์ง€(4๋Š” exclude์ž…๋‹ˆ๋‹ค) ์ •๋ ฌ์ด ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

[1, 5, 89, 255, 7, 88, 200, 123, 66]

 

1.2 ๋ณ‘๋ ฌ ์ •๋ ฌ (ParallelSort)

Arrays.sort()๊ฐ€ ์‹ฑ๊ธ€์“ฐ๋ ˆ๋“œ๋กœ ๋Œ์•„๊ฐ„๋‹ค๋ฉด Arrays.parallelSort()๋Š” ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ๋กœ ๋™์ž‘์„ ํ•ฉ๋‹ˆ๋‹ค. parallelSort๋Š” ๋ฐฐ์—ด์„ sub-array๋กœ ๋‚˜๋ˆˆ ๋’ค ๊ฐ๊ฐ์„ ๋ณ„๋„์˜ ์“ฐ๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ๊ฐ ์“ฐ๋ ˆ๋“œ์—์„œ๋Š” Arrays.sort()๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์ •๋ ฌ์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Arrays.parallelSort ์˜ ๊ฒฐ๊ณผ๋Š” ๋‹น์—ฐํžˆ Arrays.sort์˜ ๊ฒฐ๊ณผ์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋”ฉ์„ ์ด์šฉํ–ˆ๋Š๋ƒ์˜ ์ฐจ์ด๋งŒ ์กด์žฌํ•  ๋ฟ์ž…๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ Arrays.parallelSort ๋„ ๋ถ€๋ถ„์ •๋ ฌ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. Arrays.parallelSort (int [] a, int fromIndex, int toIndex);

2. List ์ •๋ ฌํ•˜๊ธฐ

๋ฆฌ์ŠคํŠธ๋ฅผ ์ •๋ ฌํ•  ๋•Œ๋Š” java.util.Collections.sort(List<T> list) ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

int[] arr = new int[] { 5, 1, 89, 255, 7, 88, 200, 123, 66 };
List<Integer> toSortList = Ints.asList(arr);    // int[]์„ Integer[]๋กœ ๋ณ€ํ™˜ (๊ตฌ๊ธ€ guava ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ด์šฉ) 
Collections.sort(toSortList);

์ถœ๋ ฅ ๊ฒฐ๊ณผ๋Š” Arrays.sort()๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด comparator๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›๋Š” sortโ€‹(List list, Comparator c) ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋„ˆ๋ฌด๋‚˜๋„ ๊ธฐ๋ณธ์ ์ธ ์–˜๊ธฐ์ง€๋งŒ ์ •๋ ฌ์„ ํ•˜๋ ค๋ฉด ๋‘ ์š”์†Œ(element)๋ฅผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ์ŠคํŠธ ๋‚ด์˜ ๋ชจ๋“  ์š”์†Œ๋“ค์ด Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Collections.sort(List<T> list) ๋Š” merge sort ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ด์šฉํ•˜์—ฌ ์ •๋ ฌ์„ ํ•ฉ๋‹ˆ๋‹ค. List.sort(Comparator c)์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅด๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค๋งŒ, ๋” ์•ˆ์ข‹๊ฒŒ ๋ณ€ํ˜•๋œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฑด ์•„๋‹ˆ๊ฒ ์ฃ . ๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋ณธ์ ์œผ๋กœ List.sort()์—์„œ ์‚ฌ์šฉํ•˜๋Š” merge sort๋Š” ์–ด๋Š์ •๋„ ๋ถ€๋ถ„ ์ •๋ ฌ์ด ๋˜์–ด์žˆ๋Š” ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด์„œ๋Š” n log(n) ๋ณด๋‹ค ํ›จ์”ฌ ์ ์€ ํšŸ์ˆ˜๋งŒ ๋น„๊ตํ•˜๋ฉฐ(๊ฑฐ์˜ ์ •๋ ฌ์ด ๋˜์–ด์žˆ๋Š” ์ƒํƒœ๋ผ๋ฉด ๊ฑฐ์˜ n๋ฒˆ์˜ ๋น„๊ต๋งŒ์œผ๋กœ๋„ ์ •๋ ฌ ๊ฐ€๋Šฅ), ๋žœ๋คํ•˜๊ฒŒ ์„ž์—ฌ์žˆ๋Š” ๋ฐ์ดํ„ฐ ์…‹์— ๋Œ€ํ•ด์„œ๋Š” ์ „ํ†ต์ ์ธ merge sort์˜ ํผํฌ๋จผ์Šค๋ฅผ ๋‚ธ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. Java14 Collections.sort() ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

๋งŒ์•ฝ ์—ญ์ˆœ(๋‚ด๋ฆผ์ฐจ์ˆœ)์œผ๋กœ ์ •๋ ฌ์„ ํ•˜๊ณ  ์‹ถ์œผ๋ฉด Collections.sort(List list, Comparator c) ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

// ๋‚ด๋ฆผ ์ฐจ์ˆœ ์ •๋ ฌ
Collections.sort(list, Comparator.reverseOrder());

์ž๋ฐ”์—์„œ๋Š” ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ์„ ์œ„ํ•ด Comparator.reverseOrder() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ธฐ๋ณธ comparator๋ฅผ ์ œ๊ณตํ•ด์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. 

3. Set ์ •๋ ฌํ•˜๊ธฐ

Set์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ LinkedHashSet์€ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜์ฃ . ๋”ฐ๋ผ์„œ Set์„ ์ •๋ ฌํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์ด LinkedHashSet ์ฒ˜๋Ÿผ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜๋Š” set ๊ตฌํ˜„์ฒด์— ๋Œ€ํ•œ ์–˜๊ธฐ์ž…๋‹ˆ๋‹ค.

Set์„ ์ •๋ ฌํ•  ๋•Œ๋„ Collections.sort()๋ฅผ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” List๋งŒ์„ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Set์„ ์ •๋ ฌํ•˜๋ ค๋ฉด List๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Set<Integer> set = new LinkedHashSet<>(Ints.asList(arr));
System.out.println(set);    // ์ •๋ ฌํ•˜๊ธฐ ์ „ ์ถœ๋ ฅ

List<Integer> list = new ArrayList<>(set);
Collections.sort(list);
set = new LinkedHashSet<>(list);

System.out.println(set);    // ์ •๋ ฌ ํ›„ ์ถœ๋ ฅ

์œ„์—์„œ ๋ณด๋“ฏ์ด list๋กœ ๋ณ€ํ™˜ ํ›„ ๋‹ค์‹œ set์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ •๋ ฌ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4. Map ์ •๋ ฌํ•˜๊ธฐ

Map์€ ์•„์‹œ๋‹ค์‹œํ”ผ key์™€ value ์Œ์œผ๋กœ ์ด๋ฃจ์–ด์ง„ ์ž๋ฃŒ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Map์„ ์ •๋ ฌํ•  ๋•Œ๋Š” key๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ value๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•˜๋Š” ๋ฐฉ๋ฒ•, ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, Map๋„ Set๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ HashMap ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ์ˆœ์„œ์™€๋Š” ๋ฌด๊ด€ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ •๋ ฌ์„ ์œ„ํ•ด์„œ๋Š” LinkedHashMap์„ ์ด์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์šฐ์„  key๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Map์„ ์ •๋ ฌํ•  ๋•Œ๋„ List, Set๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Collections.sort(List list) ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ list๋กœ ๋ณ€ํ™˜์„ ํ•ด์ฃผ์–ด์•ผ ํ•˜์ฃ . ๊ทธ๋ฆฌ๊ณ  ์ •๋ ฌ ํ›„์—๋Š” ์ˆœ์„œ์˜ ๋ณด์žฅ์„ ์œ„ํ•ด list๋ฅผ LinkedHashMap์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ List.sort(Comparator c)๋ฅผ ์ด์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์ œ์—์„œ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋ชจ๋‘ ํ™•์ธํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

// ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ์…‹
HashMap<Integer, String> map = new HashMap<>();
map.put(5, "Orange");
map.put(8, "Apple");
map.put(2, "WaterMelon");
map.put(13, "Pear");
map.put(9, "Grape");
map.put(4, "Banana");


// key, value ๋ฅผ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ entrySet()์„ ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜
List<Map.Entry<Integer, String>> entries = new ArrayList<>(map.entrySet());

// 1. List.sort ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
entries.sort(Comparator.comparing(Map.Entry::getKey));

// 2. Collections.sort ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
Collections.sort(entries, Comparator.comparing(Map.Entry::getKey));

// ์ •๋ ฌ๋œ ๋ฐ์ดํ„ฐ๋ฅผ LinkedHashMap์— ์ €์žฅ
Map<Integer, String> sortedByKey = new LinkedHashMap<>();
for (Map.Entry<Integer, String> entry : entries) {
	sortedByKey.put(entry.getKey(), entry.getValue());
}

// ์ •๋ ฌ๋œ ๋‚ด์šฉ ์ถœ๋ ฅ
System.out.println(sortedByKey);

์ •๋ ฌํ•  ๋•Œ List.sort(Comparator c) ์™€ Collections.sort(List list)๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ชจ๋‘ ์†Œ๊ฐœํ•ด๋“œ๋ ธ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์‚ฌ์šฉํ•œ Comparator.comparing() ๋ฉ”์„œ๋“œ๋Š” comparator๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค. Comparator.comparing(Map.Entry::getKey) ๋Š” Map์˜ key ๊ฐ’์„ ๋น„๊ตํ•˜๋Š” comparator๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์ฃ .

์œ„ ์ฝ”๋“œ์—์„œ ์ถœ๋ ฅ๋˜๋Š” ๋‚ด์šฉ์€ ์•„๋ž˜์™ ๊ฐ™์Šต๋‹ˆ๋‹ค.

{2=WaterMelon, 4=Banana, 5=Orange, 8=Apple, 9=Grape, 13=Pear}

key ๊ฐ’ ์ˆœ์„œ๋Œ€๋กœ ์ •๋ ฌ์ด ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿผ ์ด๋ฒˆ์—๋Š” ์œ„ ์ฝ”๋“œ๋ฅผ ์กฐ๊ธˆ ์ˆ˜์ •ํ•˜์—ฌ value๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ์„ ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

// 1. List.sort ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
entries.sort(Comparator.comparing(Map.Entry::getValue));

// 2. Collections.sort ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
Collections.sort(entries, Comparator.comparing(Map.Entry::getValue));

Comparator.comparing() ๋ฉ”์„œ๋“œ์˜ ์ธ์ž๋กœ key๊ฐ€ ์•„๋‹Œ value ๊ฐ’์„ ๋„˜๊ฒจ์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ˆ˜์ •๋œ ์ฝ”๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

{8=Apple, 4=Banana, 9=Grape, 5=Orange, 13=Pear, 2=WaterMelon}

value๊ฐ’์œผ๋กœ ๋„ฃ์–ด์ฃผ์—ˆ๋˜ ๊ณผ์ผ๋ช… ์ˆœ์œผ๋กœ ์ •๋ ฌ์ด ๋˜์–ด ์ถœ๋ ฅ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

5. ์ปค์Šคํ…€ ๊ฐ์ฒด ์ •๋ ฌํ•˜๊ธฐ

์ด๋ฒˆ์—๋Š” ์ปค์Šคํ…€ ๊ฐ์ฒด๋ฅผ ์ •๋ ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค ์ปค์Šคํ…€ ๊ฐ์ฒด๋ฅผ ์ •๋ ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ๋”ฐ๋กœ ์žˆ๋Š”๊ฑด ์•„๋‹™๋‹ˆ๋‹ค. ์ด๋ฏธ ์œ„์—์„œ ์„ค๋ช…๋“œ๋ฆฐ ๋‚ด์šฉ์ค‘์— comparator๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ผ๋‹จ ์‡ผํ•‘๋ชฐ์— ํŒ๋งค์ค‘์ธ ์ƒํ’ˆ์— ๋Œ€ํ•œ ๊ฐ์ฒด ์ •์˜๋ฅผ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@Getter
@Setter
@AllArgsConstructor
class Product {
    String name;        // ์ƒํ’ˆ ๋ช…
    int price;          // ์ƒํ’ˆ ๊ฐ€๊ฒฉ
    float sellerRating; // ํŒ๋งค์ž ํ‰์ 
    
    @Override
    public String toString() {
    	return "{" + this.name + ", " + price + ", " + sellerRating + "}";
    }
}

์ด์ œ ์ด ๊ฐ์ฒด๋ฅผ ๋ฆฌ์ŠคํŠธ๋กœ ๋งŒ๋“ค์–ด ๊ฐ€๊ฒฉ, ํŒ๋งค์ž ํ‰์ ์„ ๊ธฐ์ค€์œผ๋กœ ๊ฐ๊ฐ ์˜ค๋ฆ„์ฐจ์ˆœ, ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌ์„ ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

// ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ์…‹
List<Product> data = new ArrayList<>(Arrays.asList(
    new Product("Apple", 100, 4.3f),
    new Product("Apple", 200, 3.3f),
    new Product("Apple", 150, 4.8f)));

// 1. List.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐ€๊ฒฉ ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ
data.sort(Comparator.comparing(Product::getPrice));
System.out.println("\n1. List.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐ€๊ฒฉ ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ");
System.out.println(data);

// 2. List.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐ€๊ฒฉ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ
data.sort(Comparator.comparing(Product::getPrice, Comparator.reverseOrder()));
System.out.println("\n2. List.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐ€๊ฒฉ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ");
System.out.println(data);

// 3. Collections.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒ๋งค์ž ํŽธ์  ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ
Collections.sort(data, Comparator.comparing(Product::getSellerRating));
System.out.println("\n3. Collections.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒ๋งค์ž ํŽธ์  ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ");
System.out.println(data);

// 4. Collections.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒ๋งค์ž ํŽธ์  ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ
Collections.sort(data, Comparator.comparing(Product::getSellerRating, Comparator.reverseOrder()));
System.out.println("\n4. Collections.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒ๋งค์ž ํŽธ์  ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ");
System.out.println(data);

Product ๋ฆฌ์ŠคํŠธ๋ฅผ ํŠน์ • ํ•„๋“œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์˜ค๋ฆ„์ฐจ์ˆœ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๋Š” ์ฝ”๋“œ๋Š” List.sort๋ฅผ ์‚ฌ์šฉํ•ด๋„, Collections.sort๋ฅผ ์‚ฌ์šฉํ•ด๋„ ํ•œ ์ค„์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌ์„ ํ•˜๊ณ  ์žˆ์„ ๋•Œ๋Š” Comparator.comparing() ๋ฉ”์„œ๋“œ์— Comparator.reverseOrder() ์ธ์ž๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, Comparator.comparing(์ •๋ ฌ๊ธฐ์ค€, ์ •๋ ฌ์ˆœ์„œ) ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋˜๋Š”๊ฑฐ์ฃ .

 

์ž, ์ด์ œ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

1. List.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐ€๊ฒฉ ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ
[{Apple, 100, 4.3}, {Apple, 150, 4.8}, {Apple, 200, 3.3}]

2. List.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐ€๊ฒฉ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ
[{Apple, 200, 3.3}, {Apple, 150, 4.8}, {Apple, 100, 4.3}]

3. Collections.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒ๋งค์ž ํŽธ์  ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ
[{Apple, 200, 3.3}, {Apple, 100, 4.3}, {Apple, 150, 4.8}]

4. Collections.sort ๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒ๋งค์ž ํŽธ์  ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ
[{Apple, 150, 4.8}, {Apple, 100, 4.3}, {Apple, 200, 3.3}]

Summary

์—ฌ๊ธฐ๊นŒ์ง€ ์ž๋ฐ”์—์„œ ์ •๋ ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๊ฐ„๋žตํžˆ ์š”์•ฝ์„ ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

๋ฐฐ์—ด์˜ ์ •๋ ฌ -> Arrays.sort() ์ด์šฉ

List ์ •๋ ฌ -> Collections.sort(), List.sort() ์ด์šฉ

Set, Map ์ •๋ ฌ -> List๋กœ ๋ณ€ํ™˜ ํ›„ Collections.sort(), List.sort() ์ด์šฉ

๊ธฐ๋ณธ์ ์œผ๋กœ ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ

๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ์€ Comparator.reverseOrder() ์ด์šฉ

 

๋ฌธ์žฅ ๋‚ด์—์„œ ์‚ฌ์šฉ๋œ distinct ๋ฌธ์ž ๊ฐœ์ˆ˜ ๊ตฌํ•˜๊ธฐ

ํ•œ ๋ฌธ์žฅ ์•ˆ์—์„œ ์‚ฌ์šฉ๋œ distinct ๋ฌธ์ž์˜ ๊ฐœ์ˆ˜๋ฅผ ๊ตฌํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?

stream์ด ๋‚˜์˜ค๊ธฐ ์ „์ด์—ˆ๋‹ค๋ฉด ๊ทธ์ € String์„ ์บ๋ฆญํ„ฐ ๋ฐฐ์—ด๋กœ ๋งŒ๋“ค์–ด์„œ loop๋ฅผ ๋Œ๋ ค set์— ๋„ฃ์€ ๋’ค set์˜ ์‚ฌ์ด์ฆˆ๋ฅผ ๊ตฌํ•˜๋ฉด ๋๊ฒ ์ฃ .

์•„๋ž˜์ฒ˜๋Ÿผ ๋ง์ด์ฃ .

    String sentence = "Computer users take it for granted that their systems can do more than one thing at a time.";
    
    Set<Character> s = new HashSet<>();
    for (char c : sentence.toCharArray()) {
    	s.add(c);
    }
    
    System.out.println(s.size());

ํ•˜์ง€๋งŒ stream ์„ ์ด์šฉํ•˜๋ฉด ํ•œ์ค„๋กœ ํ•ด๊ฒฐ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

Stream์„ ์ด์šฉํ•œ distinct ๋ฌธ์ž ๊ฐœ์ˆ˜ ๊ตฌํ•˜๊ธฐ

stream์—๋Š” distinct() ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์œผ๋ฉฐ ์ด๋Š” ํ•ด๋‹น ์ŠคํŠธ๋ฆผ๋‚ด์—์„œ distinctํ•œ ๊ฒƒ๋“ค๋งŒ ์ถ”์ถœํ•ด์ค๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  count()๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ทธ ๊ฐœ์ˆ˜๋ฅผ ์…€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์œ„์—์„œ set๊ณผ for-loop๋ฅผ ์‚ฌ์šฉํ•œ ๋ถ€๋ถ„์„ stream์„ ์ด์šฉํ•˜์—ฌ ํ•œ์ค„๋กœ ์ค„์ด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    String sentence = "Computer users take it for granted that their systems can do more than one thing at a time.";

    long distinctCharsCount = sentence.chars().distinct().count();

    System.out.println(distinctCharsCount);

ํ›จ์”ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” jQuery๋ฅผ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ๋ผ๋””์˜ค ๋ฒ„ํŠผ์ด๋‚˜ ์ฒดํฌ๋ฐ•์Šค๋ฅผ ํ•ด์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

๋ผ๋””์˜ค ๋ฒ„ํŠผ ํ•ด์ œํ•˜๊ธฐ

์šฐ์„  ์•„๋ž˜์™€ ๊ฐ™์€ ํ™”๋ฉด์„ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.

html ์€ ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

<div>
    <input type="radio" name="fruit" value="orange"><label>์˜ค๋ Œ์ง€</label>
    <input type="radio" name="fruit" value="apple"><label>์‚ฌ๊ณผ</label>
    <input type="radio" name="fruit" value="banana"><label>๋ฐ”๋‚˜๋‚˜</label>
    <button onclick="deselect()">์„ ํƒํ•ด์ œ</button>
</div>

์—ฌ๊ธฐ์„œ name ๊ฐ’์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ผ์ด์˜ค ๋ฒ„ํŠผ์„ ํ•˜๋‚˜์˜ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ์–ด์ฃผ๋Š” ์†์„ฑ์ด๊ธฐ ๋•Œ๋ฌธ์ด์ฃ .

์ฒ˜์Œ์—” ์•„๋ฌด๋Ÿฐ ๋ฒ„ํŠผ์ด ์„ ํƒ์ด ์•ˆ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. 

 

๊ทธ๋ฆฌ๊ณ  ์Šคํฌ๋ฆฝํŠธ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

function deselect() {
  // name์ด fruit์ธ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์ผ๊ด„ ํ•ด์ œ
  $("input:radio[name='fruit']").prop('checked', false);
}

์„ ํƒํ•ด์ œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด deselect ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ํ•จ์ˆ˜ ๋‚ด์—์„œ๋Š” radio ๋ฒ„ํŠผํƒ€์ž…์˜ input ์ค‘์—์„œ name๊ฐ’์ด fruit์ธ ๊ฒƒ๋“ค์˜ ์ฒดํฌ์ƒํƒœ๋ฅผ false๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, fruit name์— ์†ํ•œ ๋ชจ๋“  ์ฒดํฌ์ƒํƒœ๊ฐ€ ํ•ด์ œ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์ฒดํฌ ํ•ด์ œํ•˜๊ธฐ

jQuery๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function deselect() {
    var fruits = document.getElementsByName("fruit");
    for (var i = 0; i < fruits.length; i++) {
      if (fruits[i].getAttribute('type') === 'radio') {
      		fruits[i].checked = false;
      }
    }
}

 

์ฒดํฌ๋ฐ•์Šค ์„ ํƒ ํ•ด์ œํ•˜๊ธฐ

๋งŒ์•ฝ ๋ผ๋””์˜ค ๋ฒ„ํŠผ์ด ์•„๋‹ˆ๋ผ ์ฒดํฌ๋ฐ•์Šค์ผ ๊ฒฝ์šฐ ์„ ํƒ์ž(selector)๋งŒ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

function deselect() {
  // name์ด fruit์ธ ์ฒดํฌ๋ฐ•์Šค ์ผ๊ด„ ํ•ด์ œ
  $("input:checkbox[name='fruit']").prop('checked', false);
}

 

์ฒดํฌ๋ฐ•์Šค/๋ผ๋””์˜ค๋ฒ„ํŠผ ์ผ๊ด„ ํ•ด์ œํ•˜๊ธฐ

๊ทธ๋Ÿผ ๋งŒ์•ฝ ๋ผ๋””์˜ค ๋ฒ„ํŠผ๊ณผ ์ฒดํฌ๋ฐ•์Šค๋ฅผ ๋ชจ๋‘ ํ•ด์ œํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ• ๊นŒ์š”?

selector์—์„œ radio๋‚˜ checkbox๋ผ๊ณ  ๋ช…์‹œํ•œ ๋ถ€๋ถ„๋งŒ ๋นผ์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋‹จ, ๋™์ผํ•œ name ๊ฐ’์œผ๋กœ ๋ฌถ์—ฌ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

function deselect() {
  // name์ด fruit์ธ ๋ชจ๋“  input ์˜ ์ฒดํฌ ์ƒํƒœ ์ผ๊ด„ ํ•ด์ œ
  $("input[name='fruit']").prop('checked', false);
}

์ด๋ ‡๊ฒŒ ๋ง์ด์ฃ .

 

๊ฐ„๋‹จํ•˜์ฃ ?

 

์˜ค๋Š˜๋„ ์ฆ์ฝ”ํ•˜์„ธ์š”~

 

์ž๋ฐ”์—์„œ ์ดˆ๊ฐ„๋‹จ ์บ์‹œ ์‚ฌ์šฉํ•˜๊ธฐ

 

์•ˆ๋…•ํ•˜์„ธ์š”, ์˜ค๋Š˜์€ ์บ์‹œ ๊ด€๋ จ ๋‚ด์šฉ์„ ๋“ค๊ณ  ์™”์Šต๋‹ˆ๋‹ค.

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

์šฐ์„  ํ˜„์žฌ ์ŠคํŽ™์€ ๋ฐฑ์—”๋“œ SpringFramework 4 ๊ธฐ๋ฐ˜์ด๊ณ  ํ”„๋ก ํŠธ์—”๋“œ๋Š” ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์„ ์ด์šฉํ•œ ๊ตฌ์‹ ์•ฑ์ž…๋‹ˆ๋‹ค. (์Šคํ”„๋ง ๋ถ€ํŠธ๋กœ ์ „ํ™˜ํ•˜๊ณ  ์‹ถ์ง€๋งŒ ๋” ์ค‘์š”ํ•œ ์—…๋ฌด๋“ค์ด ๋งŽ์•„ ์ƒ๊ฐ๋งŒ ํ•˜๊ณ  ์žˆ๋‹ค๋Š”...ใ…œใ…œ) AWS์˜ Tomcat 8 ํ”Œ๋žซํผ์„ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋น„์Šค ์ค‘์ด์ฃ .

 

Ehcache ์— ๋Œ€ํ•ด์„œ ์˜ค๋žœ๋งŒ์— ๊ตฌ๊ธ€๋ง์„ ํ•ด๋ณด๋‹ˆ Jcache๋ฅผ ์ง€์›ํ•œ๋‹ค๊ณ  ํ•˜์—ฌ ํ‘œ์ค€์ธ Jcache๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. Jcache๋Š” JSR-107 ํ‘œ์ค€์„ ๋”ฐ๋ฅด๋Š” ์ž๋ฐ”์—์„œ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. ์ด ํ‘œ์ค€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ๊ตฌํ˜„์ฒด๋“ค์—๋Š” Ehcache ๋ฟ ์•„๋‹ˆ๋ผ Hazelcast, Oracle Coherence, Infinispan ๋“ฑ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ž๋ฐ”์—์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์บ์‹ฑํ•˜๊ธฐ (Ehcache๋ฅผ ์ด์šฉํ•œ Jcache ์‚ฌ์šฉ๋ฒ•)

์šฐ์„  pom.xml ํŒŒ์ผ์— ์•„๋ž˜ ๋‘ ๊ฐœ์˜ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>3.9.0</version>
        </dependency>

        <dependency>
            <groupId>javax.cache</groupId>
            <artifactId>cache-api</artifactId>
            <version>1.1.1</version>
        </dependency>

ehcache, jcache ๋ชจ๋‘ ํฌ์ŠคํŒ… ์‹œ์  ์ตœ์‹ ๋ฒ„์ „์œผ๋กœ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  CacheConfig ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
import javax.cache.spi.CachingProvider;


@Configuration
public class CacheConfig {

    @Bean
    public Cache<String, String> syncCache() {

        CachingProvider provider = Caching.getCachingProvider();
        CacheManager cacheManager = provider.getCacheManager();
        MutableConfiguration<String, String> configuration =
                new MutableConfiguration<String, String>()
                        .setTypes(String.class, String.class)
                        .setStoreByValue(false)
                        .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE));
        return cacheManager.createCache("syncCache", configuration);
    }
}

์ด ์„ค์ • ๋ถ€๋ถ„์ด ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ผํ…๋ฐ์š”, ํŠนํžˆ ์บ์‹œ๋งŒ๋ฃŒ์ •์ฑ…(expiry policy) ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์€ ์ž˜ ํ™•์ธํ•˜์‹œ๋Š” ๊ฒƒ์„ ๊ถŒ์žฅ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

jcache์—์„œ ์ œ๊ณตํ•˜๋Š” ๋งŒ๋ฃŒ์ •์ฑ…์€ 5๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

 

- AccessExpiryPolicy

- CreatedExpiryPolicy

- EternalExpiryPolicy

- ModifiedExpiryPolicy

- TouchedExpiryPolicy

 

๊ฐ๊ฐ ํŠน์ • ๋งŒ๋ฃŒ์ •์ฑ…์„ ์œ„ํ•ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ๋“ค์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด EternalExpiryPolicy๋Š” ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŒ๋ฃŒ๋˜์ง€ ์•Š๋Š” ๋ฌด๊ธฐํ•œ ์ €์žฅํ•˜๋Š” ์ •์ฑ…์ด์ฃ . ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ตฌ๊ธ€๋ง ํ•ด๋ณด์‹œ๋ฉด ๋งŽ์€ ์ž๋ฃŒ๋“ค์ด ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ๋Š” CreatedExpiryPolicy๋ฅผ ์‚ฌ์šฉํ•ด์„œ Duration์œผ๋กœ 1๋ถ„์„ ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉ๋ ๋งŒํ•œ ์‹œ๊ฐ„๋“ค์ด staticํ•˜๊ฒŒ ์ •์˜๋˜์–ด ์žˆ์œผ๋‚˜ ์ •์˜๋˜์–ด์žˆ์ง€ ์•Š์€ ๋‹ค๋ฅธ ์‹œ๊ฐ„์„ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•  ๊ฒฝ์šฐ new Duration(TimeUnit timeUnit, long durationAmount) ์„ ์ด์šฉํ•˜์—ฌ ์ปค์Šคํ…€ํ•˜๊ฒŒ ์ •์˜ํ•ด์ค„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด์ œ ์ € ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•  CacheService๋ผ๋Š” ์„œ๋น„์Šค ํด๋ž˜์Šค๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค.

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import javax.cache.Cache;

@Service
@RequiredArgsConstructor
public class CacheService {

    private final Cache<String, String> syncCache;

    public boolean isCached(String key) {
        return syncCache.containsKey(key);
    }

    public void doCache(String key, String value) {
        syncCache.put(key, value);
    }

    public boolean remove(String key) {
        return syncCache.remove(key);
    }

}

 

์ด์ œ ํ•ด๋‹น ์„œ๋น„์Šค๋ฅผ ๊ฐ€์ ธ๋‹ค ์“ฐ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๊ผผ๊ผผํ•˜์‹  ๋ถ„๋“ค์€ ์ข€ ์ด์ƒํ•œ๊ฒŒ ๋ˆˆ์— ๋Œ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. 

ehcache ํŒจํ‚ค์ง€์—์„œ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•œ ๋ถ€๋ถ„์ด ํ•˜๋‚˜๋„ ์—†๋‹ค๋Š” ๊ฒƒ์ด์ฃ .

๊ทธ๋Ÿผ pom.xml ํŒŒ์ผ์—์„œ ehcache ์˜์กด์„ฑ์„ ์ œ๊ฑฐ ํ•ด๋ณผ๊นŒ์š”??

๊ทธ๋Ÿผ ์•„๋ž˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.cache.Cache]: Factory method 'syncCache' threw exception; nested exception is javax.cache.CacheException: No CachingProviders have been configured
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
	... 67 more
Caused by: javax.cache.CacheException: No CachingProviders have been configured
	at javax.cache.Caching$CachingProviderRegistry.getCachingProvider(Caching.java:391)
	at javax.cache.Caching$CachingProviderRegistry.getCachingProvider(Caching.java:361)
	at javax.cache.Caching.getCachingProvider(Caching.java:151)

 

์ฆ‰, ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋Š” API๋Š” javax.cache ํŒจํ‚ค์ง€ ๋‚ด์— ์ •์˜๋˜์–ด์žˆ๋Š” ๊ฒƒ์ด์ง€๋งŒ ๋‚ด๋ถ€์ ์œผ๋กœ ehcache์—์„œ ๊ตฌํ˜„ํ•œ ๊ตฌํ˜„์ฒด๋กœ ์‹คํ–‰๋œ๋‹ค๋Š” ๊ฒฐ๋ก ์„ ๋‚ด๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์–ด๋–ค๊ฐ€์š”? ์ •๋ง ์‰ฝ๊ฒŒ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•˜์ฃ ?

 

์ด์ƒ์œผ๋กœ ehcache๋ฅผ ํ™œ์šฉํ•œ jcache ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•œ ํฌ์ŠคํŒ…์„ ๋งˆ์นฉ๋‹ˆ๋‹ค.

 

 

 

์ฐธ๊ณ ์ž๋ฃŒ

- www.ehcache.org/

- www.ehcache.org/documentation/3.3/107.html

- www.baeldung.com/jcache

์Šคํ”„๋ง ์ˆœํ™˜์ฐธ์กฐ ๋ฌธ์ œ

์Šคํ”„๋ง์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค๋ณด๋ฉด ์—ฌ๋Ÿฌ ๋นˆ๋“ค์˜ ์ˆœํ™˜์ฐธ์กฐ๋กœ ์ธํ•ด ์•ฑ ๊ธฐ๋™์ด ์•ˆ๋˜๋Š” ์ƒํ™ฉ์— ์ข…์ข… ๋งž๋”ฑ๋“œ๋ฆฌ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ตœ๊ทผ์— ์  ํ‚จ์Šค๋กœ ๋นŒ๋“œํ•œ ์•ฑ์ด ์ˆœํ™˜์ฐธ์กฐ ๋ฌธ์ œ๋กœ ์ธํ•ด ์ •์ƒ์ ์œผ๋กœ ๊ธฐ๋™์ด ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์›ƒ๊ธด๊ฑด ๋กœ์ปฌํ™˜๊ฒฝ์—์„œ๋Š” ์•„๋ฌด๋Ÿฐ ๋ฌธ์ œ์—†์ด ์ž˜ ๋Œ์•„๊ฐ„๋‹ค๋Š” ๊ฒƒ์ด์—ˆ์ฃ . ๊ฒฐ๊ตญ ๋‹ด๋‹น์ž์˜ ํ™•์ธํ•˜์— ์  ํ‚จ์Šค์˜ ํŒŒ์ดํ”„๋ผ์ธ ์˜ต์…˜ ์„ค์ •์œผ๋กœ ํ•ด๊ฒฐ์€ ํ–ˆ์Šต๋‹ˆ๋‹ค๋งŒ, setter injection (field injection) ์ด๋ผ๋„ ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋””์ž์ธ์€ ์ข‹์ง€ ์•Š์ฃ . ๊ทธ๋ž˜์„œ ๊ด€๋ จ๋‚ด์šฉ์„ ์•Œ์•„๋ณด๋‹ค๊ฐ€ ๋„์ ์—ฌ๋ด…๋‹ˆ๋‹ค.

์ˆœํ™˜์ฐธ์กฐ๋ž€? (What is Circular Reference ? )

์ˆœํ™˜์ฐธ์กฐ๋ž€ ์„œ๋กœ ๋‹ค๋ฅธ ์—ฌ๋Ÿฌ ๋นˆ๋“ค์ด ์„œ๋กœ ๋ฌผ๊ณ ๋Š˜์–ด์ ธ์„œ ๊ณ„์† ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, ์•„๋ž˜์ฒ˜๋Ÿผ A๋Š” B์—์„œ ํ•„์š”ํ•œ๋ฐ B๋Š” ๋˜ A์—์„œ ํ•„์š”ํ•œ ์ƒํƒœ๋ฅผ ๋งํ•ฉ๋‹ˆ๋‹ค.

Bean A → Bean B → Bean A

 

๋งŒ์•ฝ Bean A -> Bean B -> Bean C ์ฒ˜๋Ÿผ ์—ฐ๊ฒฐ๋˜์–ด์žˆ๋‹ค๋ฉด ์Šคํ”„๋ง์€ A๋ฅผ ๋จผ์ € ๋งŒ๋“ค๊ณ  A๋ฅผ ํ•„์š”๋กœ ํ•˜๋Š” B๋ฅผ ๋งŒ๋“ค๊ณ  B๋ฅผ ํ•„์š”๋กœ ํ•˜๋Š” C๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์Šคํ”„๋ง์€ ์–ด๋Š ๋นˆ์„ ๋จผ์ € ์ƒ์„ฑํ•ด์•ผํ• ์ง€ ๊ฒฐ์ •ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ๋˜๊ณ  ์ˆœํ™˜์ฐธ์กฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š”๊ฑด ๊ฒฐ๊ตญ ์„ค๊ณ„๊ฐ€ ์ž˜๋ชป๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋ ‡๋‹ค๊ณ  ์„ค๊ณ„๋ฅผ ๋‹ค ๋œฏ์–ด๊ณ ์น˜์ž๋‹ˆ ๋น„์šฉ์ด ๋„ˆ๋ฌด ๋งŽ์ด ๋“ค์–ด๊ฐˆ ์ˆ˜๋„ ์žˆ์ฃ . ์ˆœํ™˜์ฐธ์กฐ ์˜ค๋ฅ˜๋Š” ์ฐธ๊ณ ๋กœ ์Šคํ”„๋ง์˜ ์˜์กด์„ฑ ์ฃผ์ž…๋ฐฉ๋ฒ• ์ค‘์—์„œ๋„ ํŠนํžˆ ์ƒ์„ฑ์ž ์ฃผ์ž…๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋นˆ ์ƒ์„ฑ์‹œ ํ•„์š”ํ•œ ๋‹ค๋ฅธ ๋นˆ์ด ์„œ๋กœ ๋ฌผ๊ณ ๋Š˜์–ด์ ธ ์žˆ์œผ๋‹ˆ ์–ด๋–ค ๋นˆ๋„ ์ƒ์„ฑ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ์ƒํ™ฉ์ด ๋˜์–ด๋ฒ„๋ฆฌ๋Š” ๊ฒƒ์ด์ฃ .

์ˆœํ™˜์ฐธ์กฐ ๋ฌธ์ œ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

์ˆœํ™˜์ฐธ์กฐ๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ํ•ด์ฃผ๋Š”๊ฒŒ ์ œ์ผ ์ข‹๊ธด ํ•˜์ง€๋งŒ ์–ด์ฉ” ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์€ ๊ผญ ์ƒ๊ธฐ๊ธฐ ๋งˆ๋ จ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ ์ˆœํ™˜์ฐธ์กฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”? ๊ฐ€์žฅ ์šฐ์„ ์‹œ ํ•ด์•ผํ•  ๊ฒƒ์€ ์ˆœํ™˜์ฐธ์กฐ์˜ ๊ณ ๋ฆฌ๋ฅผ ๋Š์–ด๋ฒ„๋ฆฌ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์Šคํ”„๋ง์—์„œ ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋ฉฐ ์„ค๊ณ„๋ฅผ ์กฐ๊ธˆ๋งŒ ๋ฐ”๊ฟ”์„œ ํ•ด๊ฒฐ์ด ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ๊ฐ€ ์ด์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋งŒ์•ฝ ์„ค๊ณ„์˜ ๋ณ€๊ฒฝ์ด ํž˜๋“  ๊ฒฝ์šฐ๋ผ๋ฉด @Lazy ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉ ํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (ํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค ๋ผ๊ณ  ์–˜๊ธฐํ•œ ์ด์œ ๋Š” ์•„๋ž˜์— ๋‚˜์˜ต๋‹ˆ๋‹ค)

@Component
public class BeanA {

    private BeanB beanB;

    @Autowired
    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }
}

 

์ด๋ ‡๊ฒŒ ๋˜์–ด์žˆ๋˜ ์ฝ”๋“œ๋ฅผ ์•„๋ž˜์ฒ˜๋Ÿผ ๋ฐ”๊ฟ”์ฃผ๋Š” ๊ฒƒ์ด์ฃ .

@Component
public class BeanA {

    private BeanB beanB;

    @Autowired
    public BeanA(@Lazy BeanB beanB) {
        this.beanB = beanB;
    }
}

 

ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. Lazy initialization ์— ๋Œ€ํ•œ ๋ฌธ์ œ๋Š” Spring ๊ณต์‹๋ฌธ์„œ(Lazy Initialization)๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํžˆ ๋ง์”€๋“œ๋ฆฌ๋ฉด ์•ฑ ๊ธฐ๋™์‹œ์ ์ด ์•„๋‹Œ ์‹ค์ œ ํ•ด๋‹น ๋นˆ์ด ํ•„์š”ํ•œ ์‹œ์ ์— ๋นˆ์„ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํŠน์ • http ์š”์ฒญ์„ ๋ฐ›์•˜์„ ๋•Œ ํž™๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์€ ์ƒํ™ฉ์ด์—ˆ๋‹ค๋ฉด ์žฅ์• ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ์–˜๊ธฐ์ฃ . ์•„๋ฌดํŠผ ์Šคํ”„๋ง์—์„œ ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š” ๋ฐฉ์‹์ด๋‹ˆ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. 

 

๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ์ƒ์„ฑ์ž ์ฃผ์ž…๋ฐฉ๋ฒ• ๋Œ€์‹ ์— setter ์ฃผ์ž…๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ setter ์ฃผ์ž… ์˜ˆ์ œ๋ฅผ ํ•œ๋ฒˆ ๋ณด์‹œ์ฃ .

@Component
public class BeanA {

    private BeanB beanB;

    @Autowired
    public void setBeanB(BeanB beanB) {
        this.beanB = beanB;
    }

    public BeanB getBeanB() {
        return beanB;
    }
}

@Component
public class BeanB {

    private BeanA beanA;

    @Autowired
    public void setBeanA(BeanA beanA) {
        this.beanA = beanA;
    }
}

 

 

ํ˜น์‹œ๋‚˜ ์ฃผ์ž…๋ฐฉ๋ฒ•์— ๋‹ค์‹œ ํ•œ๋ฒˆ ์งš๊ณ  ๋„˜์–ด๊ฐ€๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด ์Šคํ”„๋ง ์˜์กด์„ฑ ์ฃผ์ž… ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 

Conclusion

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

 

 

 

 

Reference

- www.baeldung.com/circular-dependencies-in-spring 

- docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-dependencies

์Šคํ”„๋ง ์˜์กด์„ฑ ์ฃผ์ž… (Spring Dependency Injection)

์Šคํ”„๋ง์—์„œ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํ˜„์žฌ ์„ธ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ๋Š” ์ƒ์„ฑ์ž ์ฃผ์ž… ๋ฐฉ์‹์ด๊ณ  ๋‘ ๋ฒˆ์งธ๋Š” Setter ์ฃผ์ž… ๋ฐฉ์‹, ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ Field ์ฃผ์ž… ๋ฐฉ์‹์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ˆœ์„œ๋Œ€๋กœ ํ•œ๋ฒˆ ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

1. ์ƒ์„ฑ์ž ์ฃผ์ž… (Constructor Based Dependency Injection)

๋ง ๊ทธ๋Œ€๋กœ ์ƒ์„ฑ์ž์— @Autowired๋กœ ๋‹ค๋ฅธ ๋นˆ์„ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

@Controller
public class MyController {
    private MyService myService;
 
    @Autowired
    public MyController(MyService myService) {
        this.myService = myService;
    }
}

์ฃผ์ž…ํ•ด์•ผํ•˜๋Š” ๋นˆ์ด ๋งŽ์„ ๊ฒฝ์šฐ ์ƒ์„ฑ์ž ์ž์ฒด๊ฐ€ ์ปค์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๊ฒฝ์šฐ ๋งŒ์•ฝ lombok ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Controller
@RequiredArgsConstructor
public class MyController {

    private final MyService myService;
 
}

@RequiredArgsConstructor ์–ด๋…ธํ…Œ์ด์…˜์„ class ๋ ˆ๋ฒจ์— ๋ถ™์—ฌ์ฃผ๊ณ  ์ฃผ์ž…ํ•  ๋นˆ์€ final ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์ฃผ๋ฉด lombok์—์„œ ์ž๋™์œผ๋กœ ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ์ด์šฉํ•˜์—ฌ ๋นˆ๋“ค์„ ์ฃผ์ž…ํ•ด์ค๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๋นˆ๋“ค์€ ๋ชจ๋‘ final ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์„œ ํ•„๋“œ๋กœ ์ถ”๊ฐ€๋งŒ ํ•ด์ฃผ๋ฉด ๋˜์ฃ . 

2. Setter ์ฃผ์ž… (Setter Based Dependency Injection)

Setter ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ์‹์ด์ฃ . 

@Controller
public class MyController {

    private MyService myService;
    
    @Autowired
    public void setMyService(MyService myService) {
        this.myService = myService;
    }
}

ํŠน์ดํ•œ ๊ฒฝ์šฐ๋ฅผ ์ œ์™ธํ•˜๊ณ ๋Š” ์ด ๋ฐฉ์‹์€ ๊ฑฐ์˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ชป๋ดค๋„ค์š”

3. Field ์ฃผ์ž… (์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”)

์˜ˆ์ „๋ถ€ํ„ฐ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์Šคํ”„๋ง์—์„œ ๊ถŒ๊ณ ํ•˜์ง€ ์•Š๋Š” ๋ฐฉ์‹์ด๊ธฐ๋„ ํ•˜์ฃ . 

@Controller
public class MyController {

    @Autowired
    private MyService myService;
}

ํ•„๋“œ ์ฃผ์ž… ๋ฐฉ์‹์€ ๊ณต์‹๋ฌธ์„œ์—๋„ ์†Œ๊ฐœ์กฐ์ฐจ ํ•˜๊ณ ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ณต์‹๋ฌธ์„œ์—๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹๋งŒ ์žˆ๋‹ค๊ณ  ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ฑ์ž ์ฃผ์ž… ๋ฐฉ์‹๊ณผ setter ์ฃผ์ž…๋ฐฉ์‹์ด์ฃ . ๊ทธ ์ด์œ ๋Š” ํ•„๋“œ ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜๊ฒŒ๋˜๋ฉด ์ˆœํ™˜์ฐธ์กฐ ๋ฌธ์ œ๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๊ฒŒ๋˜์–ด ์ˆœํ™˜์ฐธ์กฐ๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š๊ณ  ๊ฐœ๋ฐœ์„ ํ•˜๊ฒŒ๋˜๊ณ , ๊ทธ๋ ‡๊ฒŒ ์„ค๊ณ„๋œ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ๊ธฐ๋Šฅ์„ ํ•˜๊ฒŒ๋˜์–ด ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค๋Š” Single Responsibility ์›์น™์— ์œ„๋ฐฐ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ง€๊ฒŒ๋˜์ฃ . ๋˜ ๋‹ค๋ฅธ ์—ฌ๋Ÿฌ ๋‹จ์ ๋“ค์— ๋Œ€ํ•ด์„œ stackoverflow๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•„๋“œ ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜๊ณ  ๊ณ„์‹œ๋‹ค๋ฉด ์ƒ์„ฑ์ž ์ฃผ์ž… ๋ฐฉ์‹์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ์„ธ์š”. ๊ทธ๋Ÿฌ๋ฉด ์ˆœํ™˜์ฐธ์กฐ๋ฅผ ์ฐพ์•„๋‚ผ ์ˆ˜ ์žˆ๊ณ  ์ˆœํ™˜์ฐธ์กฐ ๊ณ ๋ฆฌ ์•ˆ์— ์žˆ๋Š” ํด๋ž˜์Šค๋“ค์„ ์‚ดํŽด๋ณด๋ฉด Single Responsibility ์›์น™์„ ์œ„๋ฐฐํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ ๋ณด์ผ๊ฒ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๊ฒƒ๋“ค์€ ํด๋ž˜์Šค ๋ถ„๋ฆฌ๋ฅผ ํ†ตํ•ด ๊ฐœ์„ ํ•ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

 

 

 

 

์ฐธ๊ณ ๋ฌธ์„œ

docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-dependencies

stackoverflow.com/questions/39890849/what-exactly-is-field-injection-and-how-to-avoid-it

 

 

 

๋งฅ๋ถ ๋ธ”๋กœ๊น…์„ ์œ„ํ•œ ์ดˆ๊ฐ„๋‹จ ๊ทธ๋ฆผ๊ทธ๋ฆฌ๊ธฐ ์•ฑ (๋งฅ๋ถ ๋ฒ„์ „ ๊ทธ๋ฆผํŒ)

์•ˆ๋…•ํ•˜์„ธ์š”, ์˜ค๋Š˜์€ ๋งฅ๋ถ์šฉ ๊ทธ๋ฆผ๊ทธ๋ฆฌ๊ธฐ ์•ฑ์„ ์†Œ๊ฐœํ•ด๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

ํฌํ† ์ƒต์ด๋‚˜ ์œ ์‚ฌ ์•ฑ๋“ค์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ค์šด ๊ทธ๋ฆผ๊ทธ๋ฆฌ๊ธฐ ์•ฑ์ด ์•„๋‹Œ ์ •๋ง ๊ฐ„๋‹จํ•œ ๊ทธ๋ฆผ๊ทธ๋ฆฌ๊ธฐ ์•ฑ์ž…๋‹ˆ๋‹ค.

์œˆ๋„์šฐ์—๋Š” ๋ณ„๋„๋กœ ์„ค์น˜ํ•˜์ง€ ์•Š์•„๋„ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋”ธ๋ ค์˜ค๋Š” ๊ทธ๋ฆผ๊ทธ๋ฆฌ๊ธฐ ํ”„๋กœ๊ทธ๋žจ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ”๋กœ "๊ทธ๋ฆผํŒ(์˜๋ฌธ์œผ๋กœ๋Š” paint)"์ด๋ผ๋Š” ํ”„๋กœ๊ทธ๋žจ ์ธ๋ฐ์š”

์ „ ๊ทธ ํ”„๋กœ๊ทธ๋žจ์„ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ณ ๋Š” "๋ˆ„๊ฐ€ ์ด๋Ÿฐ ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ๊ทธ๋ฆผ์„ ๊ทธ๋ฆฌ์ง€?" ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‹ค๊ฐ€ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๊ทธ ํ”„๋กœ๊ทธ๋žจ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ณด๊ฒŒ๋˜์—ˆ๋Š”๋ฐ์š”

๊ทธ๋ ‡๊ฒŒ ์‰ฌ์šธ ์ˆ˜๊ฐ€ ์—†๋”๊ตฐ์š”

๊ทธ ํ›„๋กœ๋Š” ์ €๋„ ๋งค์šฐ ์• ์šฉํ–ˆ๋˜ ํ”„๋กœ๊ทธ๋žจ์ด ๋ฐ”๋กœ ๊ทธ๋ฆผํŒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฐ๋ฐ ๋งฅ๋ถ์—์„œ๋Š” ๊ทธ๋Ÿฐ ํ”„๋กœ๊ทธ๋žจ์„ ์ฐพ๊ธฐ๊ฐ€ ์‰ฝ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์ตœ๊ทผ์— ๋‹ค์‹œ ์ฐพ์•„๋ณด๋‹ˆ ํŽ˜์ธํŠธ X ๋ผ๋Š” ์•ฑ์ด ์žˆ๋”๊ตฐ์š”.

์•ฑ์Šคํ† ์–ด์—์„œ paintx ๋กœ ๊ฒ€์ƒ‰์„ ํ•˜์‹œ๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์ผ๋ฐ˜ ๋ฒ„์ „๊ณผ ํ”„๋ฆฌ๋ฏธ์—„ ๋ฒ„์ „์ด ๋‚˜์˜ต๋‹ˆ๋‹ค.

๋งฅ์šฉ ๊ทธ๋ฆผ๊ทธ๋ฆฌ๊ธฐ ์•ฑ ํŽ˜์ธํŠธ X

์ €๋Š” ์ผ๋ฐ˜ ๋ฒ„์ „์„ ๋ฌด๋ฃŒ๋กœ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.

์‹คํ–‰์‹œํ‚ค๋ฉด UI ์ž์ฒด๊ฐ€ ์œˆ๋„์šฐ์˜ ๊ทธ๋ฆผํŒ๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

ํŽ˜์ธํŠธ X ์‹คํ–‰ํ™”๋ฉด

์œˆ๋„์šฐ์˜ ๊ทธ๋ฆผํŒ๊ณผ ํ•œ๋ฒˆ ๋น„๊ตํ•ด๋ณผ๊นŒ์š”?

์™€~ ์–ด์ฉœ ์ด๋ ‡๊ฒŒ ๋น„์Šทํ• ๊นŒ์š”~

์ƒ๊ธด๊ฒƒ ๋ถ€ํ„ฐ๊ฐ€ ๋„ˆ๋ฌด ๋น„์Šทํ•˜๊ณ ์š”, ๊ธฐ๋Šฅ์—ญ์‹œ ๋Œ€๋ถ€๋ถ„ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆผํŒ์„ ์ž์ฃผ ์‚ฌ์šฉํ•˜์…จ๋˜ ๋ถ„๋“ค์€ ์‰ฝ๊ฒŒ ์ ์‘ํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์—ญ์‹œ ์• ํ”Œ์˜ ์•ฑ์Šคํ† ์–ด์—๋Š” ์™„์ „๋ฌด๋ฃŒ ์•ฑ์ด ๊ฑฐ์˜ ์—†์ฃ .

๋ฌด๋ฃŒ๋กœ ๋‹ค์šด๋ฐ›์•„์„œ ์‚ฌ์šฉ์€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๊ทธ๋ฆฌ๋Š” ๊ทธ๋ฆผ์˜ ํฌ๊ธฐ๊ฐ€ ์ผ์ • ์‚ฌ์ด์ฆˆ๋ฅผ ๋„˜์–ด๊ฐ€๊ฒŒ๋˜๋ฉด ์›Œํ„ฐ๋งˆํฌ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์ข€ ๋” ์ •ํ™•ํžˆ ๋ง์”€๋“œ๋ฆฌ์ž๋ฉด ๊ฐ€๋กœ ๋˜๋Š” ์„ธ๋กœ ์–ด๋Š ํ•œ์ชฝ์ด๋ผ๋„ 200ํ”ฝ์…€์„ ๋„˜์–ด๊ฐ€๋ฉด ์›Œํ„ฐ๋งˆํฌ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค.

์ฆ‰, 1024 x 199 ๋Š” ์›Œํ„ฐ๋งˆํฌ๊ฐ€ ์ƒ๊ธฐ์ง€ ์•Š์ง€๋งŒ 199 x 200 ์‚ฌ์ด์ฆˆ์—์„œ๋Š” ์›Œํ„ฐ๋งˆํฌ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค.

ํŽ˜์ธํŠธ X Lite ์›Œํ„ฐ๋งˆํฌ

 

ํ•ด๋‹น ์›Œํ„ฐ๋งˆํฌ๋ฅผ ์—†์• ๋ ค๋ฉด ์šฐ์ธก ์ƒ๋‹จ์˜ Remove Watermark & AD ๋ฅผ ํด๋ฆญํ•˜์—ฌ ์ผ์ • ๊ธˆ์•ก์„ ์ง€๋ถˆํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

ํŽ˜์ธํŠธ X Lite ์›Œํ„ฐ๋งˆํฌ & ๊ด‘๊ณ  ์ œ๊ฑฐ ํŒ์—…์ฐฝ

์ปคํ”ผ ํ•œ์ž” ๊ฐ’๋„ ์•ˆ๋˜๋Š” $5 ๋ฅผ ๊ฒฐ์ œํ•˜๋ฉด ์›Œํ„ฐ๋งˆํฌ ๋ฐ ๊ด‘๊ณ ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ €๋Š” ๊ทธ๋ ‡๊ฒŒ ํฌ๊ฒŒ ๊ทธ๋ฆผ์„ ๊ทธ๋ฆด ์ผ์ด ์—†์–ด ์ผ๋‹จ ๋ฌด๋ฃŒ๋กœ ๊ทธ๋ƒฅ ์‚ฌ์šฉ์ค‘์ž…๋‹ˆ๋‹ค.

์–ด๋– ์‹ ๊ฐ€์š”? ๋ธ”๋กœ๊น… ์šฉ๋„๋กœ ์ž์ž˜ํ•˜๊ฒŒ ๊ทธ๋ฆผ์„ ๊ทธ๋ ค์•ผ ํ•  ๋•Œ๋„ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์ง€ ์•Š๋‚˜์š”?

 

์ด์ƒ์œผ๋กœ ๋งฅ๋ถ์šฉ ๊ทธ๋ฆผ๊ทธ๋ฆฌ๊ธฐ ์•ฑ ํŽ˜์ธํŠธX ํฌ์ŠคํŒ…์„ ๋งˆ์นฉ๋‹ˆ๋‹ค.

์˜ˆ์™ธ ์ฒ˜๋ฆฌ/์—๋Ÿฌ ํ•ธ๋“ค๋ง ์ž˜ํ•˜๊ธฐ

์•ˆ๋…•ํ•˜์„ธ์š”, ์˜ค๋Š˜์€ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ(์—๋Ÿฌ ํ•ธ๋“ค๋ง)์— ๋Œ€ํ•ด์„œ ์–˜๊ธฐ๋ฅผ ํ•ด๋ณผ๊นŒ ํ•ฉ๋‹ˆ๋‹ค.

์ฑ…์— ๋‚˜์˜ค๋Š” ๋˜๋Š” ์ธํ„ฐ๋„ท ์ƒ์—์„œ ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๋Š” try-catch ์˜ˆ์™ธ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ๋‚ด์šฉ์€ ์•„๋‹ˆ๊ณ  ์ง์ ‘ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๊ฒฝํ—˜ํ•œ ๋‚ด์šฉ์„ ํ† ๋Œ€๋กœ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ข‹๊ฒ ๋‹ค ์‹ถ์€ ๋‚ด์šฉ๋“ค์„ ๊ณต์œ ํ•˜๋Š” ์ฐจ์›์—์„œ ์ž‘์„ฑํ•˜๋Š” ๊ธ€์ž…๋‹ˆ๋‹ค. ํ˜น์—ฌ๋‚˜ ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋‚˜ ๊ณต์œ ํ•ด์ฃผ์‹œ๊ณ  ์‹ถ์€ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ๋Œ“๊ธ€๋„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

 

์šฐ์„  ์ž๋ฐ” ์„œ์ ์ด๋‚˜ ๊ตฌ๊ธ€๋ง์„ ํ•ด์„œ ์ฐพ๋Š” ์—๋Ÿฌ ํ•ธ๋“ค๋ง์— ๋Œ€ํ•œ ๋‚ด์šฉ๋“ค์€ ๋Œ€๋ถ€๋ถ„ try-catch ๋ฌธ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค์—์„œ ๋๋‚ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋‹จ์ˆœํžˆ ๊ทธ๋ ‡๊ฒŒ ์ž‘์—…ํ•˜๋ฉด ์—๋Ÿฌ ํ•ธ๋“ค๋ง์„ ์ž˜ํ•œ๋‹ค๊ณ  ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋‹ค๋ณด๋ฉด ์—ฌ๋Ÿฌ ํด๋ž˜์Šค๋“ค์„ ๋„˜๋‚˜๋“ค๋ฉด์„œ ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ๋“ค์„ ๊ฑฐ์น˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ๋ฉ”์„œ๋“œ ์ฝœ ์ฒด์ธ

์œ„ ๋ฉ”์„œ๋“œ ์ฝœ ์ฒด์ธ์€ ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค(๊ธฐ๋Šฅ)์ด ์ฒ˜๋ฆฌ๋  ๋•Œ์˜ ๊ณผ์ •์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค๋ฉด controller -> service -> dao ๋ญ ์ด๋Ÿฐ ๊ฑด๋ฐ์š”, ์•„๋ฌด๋ฆฌ ๋ณต์žกํ•œ ์„œ๋น„์Šค๋ผ ํ•˜๋”๋ผ๋„ ๊ฒฐ๊ตญ ์š”์ฒญ์—์„œ๋ถ€ํ„ฐ ์‘๋‹ต์„ ์ฃผ๊ธฐ๊นŒ์ง€์˜ ๊ณผ์ •์€ ๊ฒฐ๊ตญ ์œ„ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ํ•œ ์ค„๋กœ ์—ฐ๊ฒฐ์ด ๊ฐ€๋Šฅํ•˜์ฃ .

 

๋”ฐ๋ผ์„œ ์–ด๋Š ์‹œ์ ์— try-catch ๋กœ ์˜ˆ์™ธ๋ฅผ ์žก์•„์•ผ ํ• ์ง€ ๊ทธ๋ฆฌ๊ณ  ์˜ˆ์™ธ๋ฅผ ์žก์•˜๋‹ค๋ฉด ์–ด๋–ค ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผํ• ์ง€์— ๋Œ€ํ•ด ๋งŽ์€ ๊ณ ๋ฏผ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ƒ ๊ทธ๋Ÿฐ ๊ณ ๋ฏผ์„ ๋ณ„๋กœ ํ•˜์ง€ ์•Š๊ณ  ๋Œ€์ถฉ ์—๋Ÿฌ๋กœ๊ทธ ์ฐ์–ด๋†“๋Š” ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŽ์€ ๊ฒƒ์ด ์‚ฌ์‹ค์ด์ฃ . ์‹ฌ์ง€์–ด catch๋กœ ์žก์•„์„œ ๋กœ๊ทธ๋งŒ ๋‚จ๊ธฐ๊ณ  ๋‹ค์‹œ throw๋ฅผ ํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๋ฌด์Šจ ํญํƒ„๋Œ๋ฆฌ๊ธฐ ํ•˜๋Š” ๊ฒƒ๋„ ์•„๋‹ˆ๊ณ  ๋ง์ด์ฃ  -_-;; ์ตœ์•…์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ๊ฐœ๋ฐœ๋œ ์„œ๋น„์Šค๋Š” ํ•˜๋‚˜์˜ ์—๋Ÿฌ์— ๋Œ€ํ•ด์„œ ์ค‘๋ณต์ ์œผ๋กœ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ  ๋””๋ฒ„๊น…๋„ ๋น„ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“ค์–ด ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์€ ๊ฒƒ์ผ๊นŒ์š”?

 

์ฒซ ๋ฒˆ์งธ๋Š” ์š”์ฒญ์„ ๋ฐ›์•„์„œ ์‘๋‹ต์„ ์ฃผ๊ธฐ๊นŒ์ง€ ํ•˜๋‚˜์˜ ์ฒด์ธ์—์„œ ํŠน์ • ์˜ˆ์™ธ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋Š” ํ•œ ๊ณณ์—์„œ๋งŒ ํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ฆ‰, ์œ„ ์ฒด์ธ ๊ทธ๋ฆผ์—์„œ ๋ฉ”์„œ๋“œ 1, 2, 3, 4 ์ค‘ ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ์—์„œ๋งŒ A๋ผ๋Š” ์˜ˆ์™ธ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•œ๋‹ค๋Š”๊ฑฐ์ฃ . ์ปจํŠธ๋กค๋Ÿฌ ๋ถ€๋ถ„์—์„œ ExceptionHandler๋ฅผ ์ด์šฉํ•ด์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜๋„ ์žˆ๊ณ  ์„œ๋น„์Šค ๋ ˆ์ด์–ด์—์„œ try-catch ๋ฌธ์„ ์ด์šฉํ•ด์„œ ์ฒ˜๋ฆฌํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค. ๋ฉ”์„œ๋“œ3์—์„œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•ด๋†“์•˜๋Š”๋ฐ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋ฉ”์„œ๋“œ2์—์„œ ๋˜ ํ•˜๊ณ  ์žˆ๋‹ค๊ฑฐ๋‚˜ ํ•œ๋‹ค๋ฉด ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ œ๊ฑฐํ•˜๋Š”๊ฒŒ ์œ ์ง€๋ณด์ˆ˜ํ•˜๋Š”๋ฐ ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

 

๋‘ ๋ฒˆ์งธ๋Š” catch๋ฅผ ํ–ˆ์œผ๋ฉด ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค˜์•ผ ํ•œ๋‹ค๋Š” ๊ฒ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ "์ ์ ˆํ•œ" ์ด๋ผ๋Š” ๋ง์ด ์• ๋งคํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ์š”, ๋‹จ์ˆœํžˆ ๋กœ๊ทธ๋งŒ ์ฐ๊ณ  catchํ•œ ์˜ˆ์™ธ๋ฅผ ๋‹ค์‹œ throw ํ•˜๊ณ ์žˆ๋‹ค๋ฉด ํ•ด๋‹น try-catch๋ฌธ์€ ์“ธ๋ชจ์—†๋Š” ๊ฒƒ์ผ ๊ฐ€๋Šฅ์„ฑ์ด 99.9%์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ๋ผ๋ฉด callee์—์„œ๋งŒ catchํ•ด์„œ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ณ  ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š”๊ฒŒ ๋” ๊น”๋”ํ•ฉ๋‹ˆ๋‹ค. ๊ตณ์ด ๋‘ ๊ตฐ๋ฐ์„œ ๋‚˜๋ˆ„์–ด catchํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฑฐ์ฃ . ๋˜, ์–ด๋–ค ์˜ˆ์™ธ๋Š” ์žก์•„์„œ ๋กœ๊ทธ๋งŒ ๋‚จ๊ธฐ๊ณ  ๋ฌด์‹œํ•  ์ˆ˜๋„ ์žˆ๊ณ  (์˜ต์…”๋„ ๊ฐ’์ด ์—†๋‹ค๊ฑฐ๋‚˜ ํ•˜๋Š” ๊ฒฝ์šฐ), ์–ด๋–ค ์˜ˆ์™ธ๋Š” ๋””๋ฒ„๊น…์šฉ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋กค๋ฐฑํ•˜๋Š” ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ํ”„๋กœ์„ธ์Šค๋ฅผ ์ง„ํ–‰ํ•จ์— ์žˆ์–ด์„œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ์ง€๋Š” API์ŠคํŽ™ ๋˜๋Š” ์ •์ฑ…์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ๊ฒ๋‹ˆ๋‹ค.

 

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

 

์ œ ๊ฒฝํ—˜์„ ํ† ๋Œ€๋กœ ์‹ ์ž… ๋˜๋Š” ์ฃผ๋‹ˆ์–ด ๊ฐœ๋ฐœ์ž ๋ถ„๋“ค๊ป˜์„œ ์ด๋ ‡๊ฒŒ ํ•ด๋ณด์‹œ๋ฉด ์ข‹๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์œผ๋กœ ๋„์ ์—ฌ ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

ํ˜น์‹œ ์ด ๊ธ€์„ ์ฝ์œผ์‹œ๋‹ค๊ฐ€ ๋ฉ‹์ง„ ๋…ธํ•˜์šฐ๊ฐ€ ์žˆ์œผ์‹  ๋ถ„๋“ค์€ ๋Œ“๊ธ€๋กœ ๊ณต์œ  ๋ถ€ํƒ๋“œ๋ ค์š” ^-^

 

[์ž๋ฃŒ๊ตฌ์กฐ] AVL ํŠธ๋ฆฌ ํŠน์ง• ๋ฐ ๋กœํ…Œ์ด์…˜ ๊ธฐ์ค€ ์—์„œ AVL ํŠธ๋ฆฌ์˜ ํŠน์ง• ๋ฐ ํšŒ์ „์˜ 4๊ฐ€์ง€ ์ผ€์ด์Šค๋“ค์— ๋Œ€ํ•ด์„œ ๋ง์”€๋“œ๋ ธ์—ˆ๋Š”๋ฐ์š”

๋กœํ…Œ์ด์…˜์„ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€์— ๋Œ€ํ•ด์„œ ์ข€ ๋” ์ž์„ธํžˆ ์„ค๋ช…๋“œ๋ฆฌ๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ ํฌ์ŠคํŒ…์„ ํ•ฉ๋‹ˆ๋‹ค.

AVL ํŠธ๋ฆฌ ํšŒ์ „

์šฐ์„  ์ฒซ ๋ฒˆ์งธ ์ผ€์ด์Šค์˜€๋˜ Left-Left ์ผ€์ด์Šค์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Left-Left ์ผ€์ด์Šค๋Š” ๋ถ€๋ชจ๋…ธ๋“œ์™€ pivot ๋…ธ๋“œ๊ฐ€ ๋ชจ๋‘ left heavy ์ธ ์ผ€์ด์Šค์˜€์ฃ . 

์ฆ‰, ์‹ ๊ทœ๋กœ ์–ด๋–ค ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์„ ๋•Œ ํ•ด๋‹น ๋…ธ๋“œ์˜ ๋ถ€๋ชจ๋…ธ๋“œ์™€ pivot ๋…ธ๋“œ์˜ ๊ท ํ˜•๊ฐ’(balance factor)์ด (-)๋ผ๋Š” ์–˜๊ธฐ์ฃ .

์•„๋ž˜ ์˜ˆ์ œ๋ฅผ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

figure1. Left-Left ์—์ œ base tree

์ด๋Ÿฐ ํŠธ๋ฆฌ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด…๋‹ˆ๋‹ค. ๊ฐ ๋…ธ๋“œ์˜ ๋ฐ”๊นฅ์ชฝ์— ์”Œ์—ฌ์ง„ ์ˆซ์ž๊ฐ€ ๊ท ํ˜•๊ฐ’(balance factor)์ž…๋‹ˆ๋‹ค.

์ž, ์ด์ œ ์ด ํŠธ๋ฆฌ์— 2๋ฒˆ ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

figure2. Left-Left ์˜ˆ์ œ ์‹ ๊ทœ๋…ธ๋“œ ์ถ”๊ฐ€

์ƒˆ๋กญ๊ฒŒ ๋…ธ๋“œ๊ฐ€ ์ถ”๊ฐ€๊ฐ€ ๋˜๋ฉด์„œ 5๋ฒˆ ๋…ธ๋“œ์™€ 10๋ฒˆ ๋…ธ๋“œ์˜ ๊ท ํ˜•๊ฐ’์— ๋ณ€๋™์ด ์ƒ๊ธฐ๊ณ  10๋ฒˆ ๋…ธ๋“œ์˜ ๊ท ํ˜•๊ฐ’์ด -2๊ฐ€ ๋˜๋ฉด์„œ AVL ํŠธ๋ฆฌ์˜ ์กฐ๊ฑด์— ์œ„๋ฐฐ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํšŒ์ „์„ ํ•ด์•ผํ•˜๋Š”๋ฐ ์ด๋ ‡๊ฒŒ ์ƒˆ๋กœ ์ถ”๊ฐ€ํ•œ ๋…ธ๋“œ์˜ ๋ถ€๋ชจ ๋…ธ๋“œ(5๋ฒˆ ๋…ธ๋“œ)์™€ pivot ๋…ธ๋“œ(10๋ฒˆ ๋…ธ๋“œ)๊ฐ€ ๋ชจ๋‘ left heavy์ธ ๊ฒฝ์šฐ์—๋Š” ์˜ค๋ฅธ์ชฝ์œผ๋กœ 1ํšŒ์ „์„ ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

figure3. Left-Left ์˜ˆ์ œ ๋กœํ…Œ์ด์…˜ํ•˜๊ธฐ

์˜ค๋ฅธ์ชฝ์œผ๋กœ ํšŒ์ „์„ ํ•ด์ฃผ๊ฒŒ๋˜๋ฉด ๋ชจ๋“  ๋…ธ๋“œ์˜ ๊ท ํ˜•๊ฐ’์ด 0์ด ๋˜๋ฉด์„œ AVL ํŠธ๋ฆฌ์˜ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๋‘ ๋ฒˆ์งธ๋กœ ์™ผ์ชฝ์œผ๋กœ ํšŒ์ „ํ•ด์•ผํ•˜๋Š” Right-Right ์ผ€์ด์Šค๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Right-Right ์ผ€์ด์Šค๋Š” ๋ถ€๋ชจ๋…ธ๋“œ์™€ pivot ๋…ธ๋“œ๊ฐ€ ๋ชจ๋‘ right heavy ์ธ ์ผ€์ด์Šค์ด์ฃ .

์•„๋ž˜ ๊ทธ๋ฆผ์€ right-right ์ผ€์ด์Šค์— ๋Œ€ํ•ด์„œ ์™ผ์ชฝ์œผ๋กœ ํšŒ์ „ํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

figure4. AVL ํŠธ๋ฆฌ์˜ ์™ผ์ชฝ ํšŒ์ „ (์ถœ์ฒ˜: tutorialspoint.com)

์ตœ์ดˆ์— A, B ๋…ธ๋“œ๊ฐ€ ์žˆ์—ˆ๊ณ  C ๋…ธ๋“œ๊ฐ€ ์ถ”๊ฐ€๊ฐ€ ๋˜๋ฉด์„œ right unbalanced ํŠธ๋ฆฌ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

right-right ์ผ€์ด์Šค์— ํ•ด๋‹นํ•˜๋ฏ€๋กœ ์™ผ์ชฝ์œผ๋กœ 1ํšŒ์ „ ํ•ด์ฃผ๋ฉด balanced ํŠธ๋ฆฌ๊ฐ€ ๋˜์ฃ .

 

์ด์ œ ์„ธ ๋ฒˆ์งธ ์ผ€์ด์Šค์ธ left-right ์ผ€์ด์Šค๋ฅผ ํ•œ๋ฒˆ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

left-right ์ผ€์ด์Šค๋Š” ๋ถ€๋ชจ ๋…ธ๋“œ์™€ pivot ๋…ธ๋“œ์˜ ๊ท ํ˜•๊ฐ’์ด ๊ฐ๊ฐ right-heavy, left-heavy, ์ธ ๊ฒฝ์šฐ๋ผ๊ณ  ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ง์€ ๊ฒฐ๊ตญ ์–ด๋–ค ๋…ธ๋“œ์˜ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์— ์ƒˆ๋กœ์šด ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฒŒ ๋˜๋Š” ๊ฒฝ์šฐ๋ผ๊ณ  ์ดํ•ดํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ ๊ทธ๋ฆผ์„ ๋ณด์‹œ๋ฉด ์ดํ•ด๊ฐ€ ๋” ๋น ๋ฅผ ๊ฒ๋‹ˆ๋‹ค.

figure5. left-right ์ผ€์ด์Šค

์ตœ์ดˆ์— C์™€ A ๋…ธ๋“œ๋งŒ ์žˆ์—ˆ๋Š”๋ฐ B ๋…ธ๋“œ๊ฐ€ ์ถ”๊ฐ€๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด A๋…ธ๋“œ์˜ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๊ฒ ์ฃ . ์œ„ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ๋ง์ด์ฃ .

figure6. ์™ผ์ชฝ ํšŒ์ „

left-right ์ผ€์ด์Šค๋ผ๋ฉด ์™ผ์ชฝ์œผ๋กœ ํšŒ์ „ํ–ˆ๋‹ค๊ฐ€ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ํšŒ์ „ํ•˜๋ฉด ๋˜๋ฏ€๋กœ ์œ„ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ์™ผ์ชฝ์œผ๋กœ ํšŒ์ „์„ ์‹œ์ผœ๋ด…๋‹ˆ๋‹ค.

figure7. ์™ผ์ชฝ ํšŒ์ „์˜ ๊ฒฐ๊ณผ

์™ผ์ชฝ์œผ๋กœ ํšŒ์ „์„ ์‹œํ‚ค๋ฉด ์œ„ ๊ทธ๋ฆผ ์ฒ˜๋Ÿผ ๋ฉ๋‹ˆ๋‹ค. ์–ด๋ž? ์–ด๋””์„œ ๋ณธ ๊ทธ๋ฆผ์ด๋„ค์š”?

๋งž์Šต๋‹ˆ๋‹ค. ์ œ์ผ ์ฒ˜์Œ์— ๋ดค๋˜ left-left ์ผ€์ด์Šค์™€ ๋˜‘๊ฐ™์•„ ์กŒ์Šต๋‹ˆ๋‹ค.

left-left ์ผ€์ด์Šค๋ฉด ์˜ค๋ฅธ์ชฝ์œผ๋กœ ํšŒ์ „์„ ์‹œ์ผœ์•ผ๊ฒ ์ฃ ?

figure8. ์˜ค๋ฅธ์ชฝ ํšŒ์ „
figure9. ๊ท ํ˜• ํŠธ๋ฆฌ

์ด์ œ ๊ท ํ˜•์ด ๋ชจ๋‘ ๋งž์ถฐ์กŒ์Šต๋‹ˆ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ right-left ์ผ€์ด์Šค๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•˜๊ฒŒ๋Š” left-right ์ผ€์ด์Šค์™€ ๋ฐ˜๋Œ€๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. 

left-right ์ผ€์ด์Šค๊ฐ€ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์— ์ƒˆ๋กœ์šด ๋…ธ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜๋Š” ์ผ€์ด์Šค์˜€์œผ๋‹ˆ,

right-left ์ผ€์ด์Šค๋Š” ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์— ์ƒˆ๋กœ์šด ๋…ธ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜๋Š” ์ผ€์ด์Šค๊ฒ ์ฃ .

๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋ง์€ ๋ถ€๋ชจ ๋…ธ๋“œ์™€ pivot ๋…ธ๋“œ์˜ ๊ท ํ˜•๊ฐ’์ด ๊ฐ๊ฐ left-heavy, right-heavy๋ผ๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆผ์œผ๋กœ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

figure10. right-left ์ผ€์ด์Šค

A, C ๋…ธ๋“œ๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ B ๋…ธ๋“œ๊ฐ€ ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. C ๋…ธ๋“œ์˜ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ๋กœ ์ถ”๊ฐ€๊ฐ€ ๋˜์ฃ .

A๋…ธ๋“œ์˜ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ(C๊ฐ€ ๋ฃจํŠธ๋…ธ๋“œ์ธ)์˜ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์— ์ƒˆ๋กœ์šด ๋…ธ๋“œ๊ฐ€ ์ถ”๊ฐ€๊ฐ€ ๋์Šต๋‹ˆ๋‹ค.

figure11. ์˜ค๋ฅธ์ชฝ ํšŒ์ „

์ด๋Ÿฐ ๊ฒฝ์šฐ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ๋จผ์ € ํšŒ์ „์„ ํ•ด์ค๋‹ˆ๋‹ค.

figure12. ์˜ค๋ฅธ์ชฝ ํšŒ์ „ ๊ฒฐ๊ณผ

๊ทธ๋Ÿฌ๋ฉด?? ๋‘ ๋ฒˆ์งธ ์ผ€์ด์Šค์ธ right-right ์ผ€์ด์Šค๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์™ผ์ชฝ์œผ๋กœ ํšŒ์ „์‹œ์ผœ์ฃผ๋ฉด ๋˜๊ฒ ์ฃ ?

figure13. ์™ผ์ชฝ ํšŒ์ „
figure14. ๊ท ํ˜• ํŠธ๋ฆฌ

์ž, ์ด๋ ‡๊ฒŒ ๋งˆ์ง€๋ง‰ ๋„ค ๋ฒˆ์งธ ์ผ€์ด์Šค๋„ ๊ท ํ˜• ํŠธ๋ฆฌ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

AVL ํŠธ๋ฆฌ์˜ 4๊ฐ€์ง€ ํšŒ์ „ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ๊ทธ๋ฆผ์œผ๋กœ ์„ค๋ช…์„ ํ•ด๋“œ๋ ธ๋Š”๋ฐ์š”,

์ดํ•ด๊ฐ€ ์ž˜ ๋˜์…จ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 

์ด์ƒ์œผ๋กœ AVL ํŠธ๋ฆฌ์˜ ๋กœํ…Œ์ด์…˜๋ฒ•์— ๋Œ€ํ•œ ํฌ์ŠคํŒ…์„ ๋งˆ์น˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์˜ค๋Š˜๋„ ์ฆํ”„ํ•˜์„ธ์š”~

 

 

Note: left-left ์ผ€์ด์Šค๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ์ผ€์ด์Šค๋“ค์— ๋Œ€ํ•œ ์ด๋ฏธ์ง€๋Š” tutorialspoint.com ์—์„œ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.

 

AVL ํŠธ๋ฆฌ ํŠน์ง• ๋ฐ ๋กœํ…Œ์ด์…˜ ๊ธฐ์ค€

AVL ํŠธ๋ฆฌ๋Š” ์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ(Binary Search Tree, BST)์˜ ํ•œ ์ข…๋ฅ˜์ž…๋‹ˆ๋‹ค.

์ด์ง„ ํƒ์ƒ‰ ํŠธ๋ฆฌ(BST)๋Š” ์ด์ง„ํŠธ๋ฆฌ(Binary Tree)์˜ ํ•œ ์ข…๋ฅ˜์ด์ฃ .

๋”ฐ๋ผ์„œ AVL ํŠธ๋ฆฌ๋Š” ์ด์ง„ํŠธ๋ฆฌ์™€ ์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ์˜ ํŠน์ง•๋“ค์„ ๋ชจ๋‘ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ๊ทธ ํŠน์ง•๋“ค์ด ๋ฌด์—‡์ผ๊นŒ์š”?

 

- ์ด์ง„ํŠธ๋ฆฌ(Binary Tree)์˜ ํŠน์ง•

a. ๊ฐ ๋…ธ๋“œ๋Š” ์ตœ๋Œ€ 2๊ฐœ์˜ ์ž์‹ ๋…ธ๋“œ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.

b. ์ž์‹ ๋…ธ๋“œ๋Š” ๋ณดํ†ต ์™ผ์ชฝ๊ณผ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค -> left child, right child

c. ๊ฐ ๋…ธ๋“œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. (๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ ์ค‘ ํ•˜๋‚˜์ด๋‹ˆ ๋‹น์—ฐํ•œ ๋ง์ด๋‹ค)

 

- ์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ(Binary Search Tree, BST)์˜ ํŠน์ง•

a. ์ด์ง„ํŠธ๋ฆฌ์ด๋ฏ€๋กœ ์ด์ง„ํŠธ๋ฆฌ์˜ ํŠน์„ฑ์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ–๊ณ  ์žˆ๋‹ค.

b. ๊ฐ ๋…ธ๋“œ์˜ ๊ฐ’์€ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ๊ฐ’๋“ค ๋ณด๋‹ค ํฌ๊ณ  ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ๊ฐ’๋“ค ๋ณด๋‹ค ์ž‘๋‹ค.

c. ๋”ฐ๋ผ์„œ ์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ๋Š” ์ •๋ ฌ๋œ ์ด์ง„ํŠธ๋ฆฌ(sorted binary tree)๋ผ๊ณ ๋„ ํ•œ๋‹ค. 

 

์ž, ๊ทธ๋ฆฌ๊ณ  ์—ฌ๊ธฐ์— ์ถ”๊ฐ€๋กœ ์•„๋ž˜์™€ ๊ฐ™์€ ํŠน์ง•์„ ๊ฐ–๋Š” ํŠธ๋ฆฌ๋ฅผ AVL ํŠธ๋ฆฌ๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

- AVL ํŠธ๋ฆฌ์˜ ํŠน์ง•

a. ์–ด๋–ค ๋…ธ๋“œ๋ผ๋„ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์™€ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋†’์ด ์ฐจ์ด๊ฐ€ 1๋ณด๋‹ค ํฌ์ง€ ์•Š๋‹ค. (์ตœ๋Œ€ ๋†’์ด ์ฐจ์ด๋Š” 1 ์ด๋‹ค.)

=> Binary Tree์ด๋ฉด์„œ ์ตœ๋Œ€ ๋†’์ด ์ฐจ์ด๊ฐ€ 1์ด๋ผ๋Š” ๋ง์€ height = O(log N) ์ด๋ผ๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค.

b. ๋งŒ์•ฝ ํŠน์ • ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ–ˆ์„ ๋•Œ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์™€ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋†’์ด ์ฐจ์ด๊ฐ€ 1๋ณด๋‹ค ์ปค์ง€๋Š” ๋…ธ๋“œ๊ฐ€ ์ƒ๊ธด๋‹ค๋ฉด, re-balancing์„ ํ†ตํ•ด a ๊ทœ์น™์— ์–ด๊ธ‹๋‚˜์ง€ ์•Š๋„๋ก ํ•œ๋‹ค.

=> ์ด ๋ง์€ AVL ํŠธ๋ฆฌ๋Š” ์ž๊ฐ€ ๊ท ํ˜• ์ด์ง„ ํƒ์ƒ‰ ํŠธ๋ฆฌ(self-balancing BST) ๋ผ๋Š” ๋ง๊ณผ ๊ฐ™๋‹ค.

c. ๊ท ํ˜• ํŠธ๋ฆฌ(balanced tree)์ด๋‹ค. (์ข€๋” ์ •ํ™•ํžˆ๋Š” ๋†’์ด ๊ท ํ˜• ํŠธ๋ฆฌ์ด๋‹ค.)

=> ๊ท ํ˜• ํŠธ๋ฆฌ๋Š” ์•„๋ž˜ ์„ธ ๊ฐ€์ง€ ์กฐ๊ฑด์„ ๋ชจ๋‘ ๋งŒ์กฑํ•˜๋Š” ํŠธ๋ฆฌ์ด๋‹ค.

   i. ์™ผ์ชฝ๊ณผ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋†’์ด ์ฐจ์ด๊ฐ€ ์ตœ๋Œ€ 1์ด๋‹ค.

  ii. ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ๊ฐ€ ๊ท ํ˜• ํŠธ๋ฆฌ์ด๋‹ค.

 iii. ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ๊ฐ€ ๊ท ํ˜• ํŠธ๋ฆฌ์ด๋‹ค.

d. ๊ฐ ๋…ธ๋“œ๋Š” ๊ท ํ˜•๊ฐ’(๊ท ํ˜•์ธ์ˆ˜, balance factor)์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ ์ด ๊ท ํ˜•๊ฐ’์€ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋†’์ด์—์„œ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋†’์ด๋ฅผ ๋บ€ ๊ฐ’์ด๋‹ค. ์ด ๊ฐ’์€ ํ•ญ์ƒ {-1, 0, 1 } ์…‹ ์ค‘ ํ•˜๋‚˜์˜ ๊ฐ’์ด์–ด์•ผ ํ•œ๋‹ค.

e. ๊ท ํ˜•๊ฐ’(๊ท ํ˜•์ธ์ˆ˜, balance factor)์ด ๋งˆ์ด๋„ˆ์Šค(-)์ด๋ฉด left-heavy, ํ”Œ๋Ÿฌ์Šค(+) ๊ฐ’์„ ๊ฐ€์ง€๋ฉด right-heavy ๋ผ๊ณ  ํ•œ๋‹ค.

f. pivot ๋…ธ๋“œ๋Š” ์ƒˆ๋กœ์šด ๋…ธ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์„ ๋•Œ ๊ท ํ˜•๊ฐ’์— ๋ณ€๋™์ด ๋ฐœ์ƒํ•˜์—ฌ unbalanced ๋œ ๋…ธ๋“œ๋“ค ์ค‘ ์‹ ๊ทœ๋…ธ๋“œ์™€ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๋…ธ๋“œ๋ฅผ ๋งํ•œ๋‹ค. 

 

์ž, ์œ„์™€ ๊ฐ™์€ ํŠน์ง•์„ ๊ฐ–๊ณ  ์žˆ๋Š” AVL ํŠธ๋ฆฌ์— ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ ๋˜๋Š” ์‚ญ์ œ๋ฅผ ํ•˜๊ฒŒ๋˜๋ฉด ์Šค์Šค๋กœ ๊ท ํ˜•์„ ๋งž์ถ”๊ธฐ ์œ„ํ•ด ํŠธ๋ฆฌ์˜ ํšŒ์ „(rotation)์„ ํ•„์š”๋กœ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋…ธ๋“œ๊ฐ€ ์ถ”๊ฐ€ ๋˜๋Š” ์‚ญ์ œ ๋˜๋ฉด์„œ ์ผ๋ถ€ ๋…ธ๋“œ๋“ค์˜ ๊ท ํ˜•๊ฐ’์— ๋ณ€๋™์ด ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํšŒ์ „์„ ํ•„์š”๋กœ ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ํฌ๊ฒŒ 2๊ฐ€์ง€(single rotation๊ณผ double rotation) ์ข€ ๋” ์„ธ๋ถ„ํ™”ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด 4๊ฐ€์ง€๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์™ผ์™ผ (Left - Left) : ๋ถ€๋ชจ ๋…ธ๋“œ์™€ pivot ๋…ธ๋“œ์˜ ๊ท ํ˜•๊ฐ’์ด ๋ชจ๋‘ left-heavy์ธ ๊ฒฝ์šฐ
    => Single Right rotation (์˜ค๋ฅธ์ชฝ์œผ๋กœ 1ํšŒ์ „)
  • ์˜ค์˜ค (Right - Right) : ๋ถ€๋ชจ ๋…ธ๋“œ์™€ pivot ๋…ธ๋“œ์˜ ๊ท ํ˜•๊ฐ’์ด ๋ชจ๋‘ right-heavy์ธ ๊ฒฝ์šฐ
    => Single Left rotation (์™ผ์ชฝ์œผ๋กœ 1ํšŒ์ „)
  • ์™ผ์˜ค (Left - Right) : ๋ถ€๋ชจ ๋…ธ๋“œ์™€ pivot ๋…ธ๋“œ์˜ ๊ท ํ˜•๊ฐ’์ด ๊ฐ๊ฐ right-heavy, left-heavy, ์ธ ๊ฒฝ์šฐ
    => Double Left-Right rotation (์™ผ์ชฝ์œผ๋กœ ํ•œ๋ฒˆ, ์˜ค๋ฅธ์ชฝ์œผ๋กœ ํ•œ๋ฒˆ ์ด ๋‘ ๋ฒˆ์˜ ํšŒ์ „)
  • ์˜ค์™ผ(Right - Left) : ๋ถ€๋ชจ ๋…ธ๋“œ์™€ pivot ๋…ธ๋“œ์˜ ๊ท ํ˜•๊ฐ’์ด ๊ฐ๊ฐ left-heavy, right-heavy์ธ ๊ฒฝ์šฐ
    => Double Right-Left rotation (์˜ค๋ฅธ์ชฝ์œผ๋กœ ํ•œ๋ฒˆ, ์™ผ์ชฝ์œผ๋กœ ํ•œ๋ฒˆ ์ด ๋‘ ๋ฒˆ์˜ ํšŒ์ „)

 

๊ฐ๊ฐ์˜ ๋กœํ…Œ์ด์…˜์— ๋Œ€ํ•œ ๊ทธ๋ฆผ์€ wiki์— ๋งค์šฐ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋‚˜์™€์žˆ์–ด gif ์ด๋ฏธ์ง€๋ฅผ ์ฒจ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.

AVL ํŠธ๋ฆฌ์˜ 4๊ฐ€์ง€ ๋กœํ…Œ์ด์…˜ ์˜ˆ์ œ (์ถœ์ฒ˜: https://en.wikipedia.org/wiki/AVL_tree)

๋กœํ…Œ์ด์…˜ ์˜ˆ์ œ ๊ทธ๋ฆผ์ด ๋„ˆ๋ฌด ๊ฐ„๋‹จํ•ด์„œ ์œ„ 4๊ฐ€์ง€ ์ผ€์ด์Šค์— ๋Œ€ํ•ด ์„ค๋ช…์ด ์ž˜ ๋ ์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๋กœํ…Œ์ด์…˜์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์„ค๋ช…์€ ๋‹ค์Œ ํฌ์ŠคํŒ…์„ ๊ธฐ๋‹ค๋ ค์ฃผ์„ธ์š”~

 

์ถ”๊ฐ€๋กœ AVL ํŠธ๋ฆฌ์— ์ƒˆ๋กœ์šด ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•˜๋Š” ๋™์ž‘์€ ์ตœ์•…์˜ ์ƒํ™ฉ์—์„œ๋„ O(log N) ์˜ ์‹œ๊ฐ„๋ณต์žก๋„๋ฅผ ๋ณด์ด๋ฉฐ ๊ฒ€์ƒ‰ ๋˜ํ•œ O(log N) ์œผ๋กœ ๋น ๋ฅธ ์ž๋ฃŒ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

 

 

 

 

AWS ๋กœ๊ณ 

AWS ์—˜๋ผ์Šคํ‹ฑ์„œ์น˜ ํ‚ค๋ฐ”๋‚˜ ์—๋Ÿฌ - Unable to find saved objects

์ตœ๊ทผ AWS์˜ ์—˜๋ผ์Šคํ‹ฑ์„œ์น˜ ํด๋Ÿฌ์Šคํ„ฐ ์ด์Šˆ ๋•Œ๋ฌธ์— ์‹œ๊ฐ„์„ ๋งŽ์ด ๋‚ญ๋น„ํ–ˆ๋‹ค.

AWS์˜ elastic search๋Š” ํด๋Ÿฌ์Šคํ„ฐ์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ๊ฐœ๋ฐœ์ž๊ฐ€ ์–ด๋–ป๊ฒŒ ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ๊ณค๋ž€ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํด๋Ÿฌ์Šคํ„ฐ๋„ ํ‚ค๋ฐ”๋‚˜๋„ ์žฌ์‹œ์ž‘์„ ํ•  ์ˆ˜ ์—†๊ณ  ๋ฌด์กฐ๊ฑด AWS internal team์—์„œ ํ™•์ธํ•ด์ค˜์•ผํ•œ๋‹ค.

support์— chat์œผ๋กœ ๋ฌธ์˜ํ•ด๋„ ๊ฒฐ๊ตญ internal team์œผ๋กœ ๋„˜์–ด๊ฐ„๋‹ค.

 

๋‹คํ–‰ํžˆ ํ‚ค๋ฐ”๋‚˜๊ฐ€ ์ ‘์†์ด ๋œ๋‹ค๋ฉด ํด๋Ÿฌ์Šคํ„ฐ ์ด์Šˆ๋Š” ์•„๋‹ˆ๋‹ค.

๊ทผ๋ฐ ํ‚ค๋ฐ”๋‚˜์˜ discover ๋ฉ”๋‰ด์—์„œ ์•„๋ฌด๊ฒƒ๋„ ์กฐํšŒ๊ฐ€ ์•ˆ๋˜๊ณ  ํ•˜์–€ ํ™”๋ฉด์ด ๋œจ๋Š” ๊ฒฝ์šฐ๋ฅผ ๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค.

ํด๋Ÿฌ์Šคํ„ฐ ์ƒํƒœ์™€ ํ‚ค๋ฐ”๋‚˜์˜ ์ƒํƒœ๋Š” ๋ชจ๋‘ GREEN ์ด์—ˆ๋‹ค.

ํŠน๋ณ„ํžˆ ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ํ•˜์ง€ ์•Š์•˜์—ˆ๋Š”๋ฐ๋„ ๊ฐ‘์ž๊ธฐ ์ด๋Ÿฐ ํ˜„์ƒ์ด ์ƒ๊ฒผ๋‹ค.

ES ํด๋Ÿฌ์Šคํ„ฐ ์ธ์Šคํ„ด์Šค ํƒ€์ž…์„ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ณด์•˜์œผ๋‚˜ ํ˜„์ƒ์€ ๋™์ผํ–ˆ๋‹ค.

๊ทธ๋ž˜์„œ ํ‚ค๋ฐ”๋‚˜ ์ธ๋ฑ์Šค๋ฅผ ์‚ญ์ œํ•˜์—ฌ ์„ค์ •์„ ์ดˆ๊ธฐํ™”ํ•ด๋ณด์•˜๋‹ค. 

์ธ๋ฑ์Šค ํŒจํ„ด์„ ์ƒˆ๋กœ ์ •์˜ํ•ด์ฃผ๊ณ  ๋‹ค์‹œ discover๋กœ ๋“ค์–ด๊ฐ”๋Š”๋ฐ....์˜ค์ž‰?

๋‹ค์‹œ ์ธ๋ฑ์Šค ํŒจํ„ด์„ ์ •์˜ํ•˜๋ผ๋Š” ํ™”๋ฉด์ด ๋‚˜์˜จ๋‹ค.;;; ํ—;;;

๋ฐฉ๊ธˆ ์ •์˜ํ•ด์ค€ ๊ฒƒ์— ๋Œ€ํ•ด ์ œ๋Œ€๋กœ ์ธ์‹์„ ๋ชปํ•œ๋‹ค;;;;

saved objects ๋ฅผ ๋“ค์–ด๊ฐ€๋ณด๋‹ˆ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

Unable to find saved objects

 

์ด๊ฒƒ์ €๊ฒƒ ํ•ด๋ณด๋‹ค๊ฐ€ AWS support์— live chat์œผ๋กœ ๋ฌธ์˜ํ–ˆ๋‹ค.

GET _cat/aliases ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ผ๊ณ  ํ•œ๋‹ค.

์‹คํ–‰ํ•ด๋ณด๋‹ˆ ๋ณ„๋‹ค๋ฅธ ๋ฉ”์‹œ์ง€๋Š” ์—†๊ณ  ๋‹จ์ˆœํžˆ 200 success ํ‘œ์‹œ๋งŒ ํ‚ค๋ฐ”๋‚˜ ์ฐฝ ์šฐ์ƒ๋‹จ์— ํ‘œ์‹œ๋˜์—ˆ๋‹ค.

๊ทธ ์–˜๊ธธ ํ•ด์ฃผ๋‹ˆ alias๊ฐ€ ์ œ๋Œ€๋กœ ์—ฐ๊ฒฐ์ด ์•ˆ๋œ๊ฒƒ ๊ฐ™๋‹ค๋ฉด์„œ ์•„๋ž˜ ๋งํฌ๋ฅผ ํ•˜๋‚˜ ๋˜์ ธ์ฃผ๋ฉด์„œ ๋ชจ๋“  ์Šคํ…์„ ๊ทธ๋Œ€๋กœ ๋”ฐ๋ผ์„œ ํ•ด๋ณด๋ผ๊ณ  ํ•œ๋‹ค.

 

aws.amazon.com/ko/premiumsupport/knowledge-center/amazon-es-saved-objects-kibana/

 

Kibana์—์„œ Amazon ES๋กœ๋ถ€ํ„ฐ ์ €์žฅํ•œ ๊ฐ์ฒด ์ฐพ๊ธฐ

Amazon ES ์‚ฌ์šฉ์ž๋“ค์€ Amazon ES ๋ฒ„์ „ 7.1๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•œ ํ›„์— ๊ฐ€๋” 400 Bad Request ์˜ค๋ฅ˜๋ฅผ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—…๊ทธ๋ ˆ์ด๋“œ์— ๋”ฐ๋ผ ์‚ฌ์šฉ์ž๊ฐ€ Kibana์—์„œ ์ €์žฅ๋œ ๊ฐ์ฒด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ

aws.amazon.com

์Šคํ…์„ ๋”ฐ๋ผํ•˜๋ฉด์„œ ๋ฌด์Šจ ๋ช…๋ น์–ด์ธ๊ฐ€ ๋ณด์•˜๋”๋‹ˆ ์ƒˆ๋กœ์šด ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“ค๊ณ  alias๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ๊ณ  ๊ธฐ์กด ์ธ๋ฑ์Šค์˜ ๋‚ด์šฉ์„ ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

 

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

dev tools๋กœ ๋Œ์•„์™€ GET _cat/aliases ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋‹ˆ 200 success ํ‘œ์‹œ์™€ ํ•จ๊ป˜ .kibana_1.kibana .kibana_1 - - - - ๋ผ๊ณ  ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋˜์—ˆ๋‹ค.

 

์˜ค๋Š˜๋„ ์—๋Ÿฌ์™€ ๋งˆ์ฃผํ•œ ์ฆ๊ฑฐ์šด ํ•˜๋ฃจ์˜€๋‹ค.

reactjs ํ”„๋ก ํŠธ์™€ java ๋ฐฑ์—”๋“œ ์—ฐ๋™

์ด๋ฒˆ์—๋Š” ReactJS๋กœ ๊ตฌํ˜„ํ•œ ํ”„๋ก ํŠธ์—”๋“œ์™€ Java + ์Šคํ”„๋ง๋ถ€ํŠธ๋กœ ๊ตฌํ˜„ํ•œ ๋ฐฑ์—”๋“œ๋ฅผ ์—ฐ๋™ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

react๋ฅผ ์“ฐ๋‹ˆ ์ผ๋ฐ˜์ ์ธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์„ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ๋„ ์ž˜ ๋ชจ๋ฅด๊ฒ ๊ณ , ajax ํ˜ธ์ถœ์„ jquery๋กœ๋งŒ ํ–ˆ์—ˆ๋Š”๋ฐ fetch ๋ฉ”์„œ๋“œ๋ฅผ ์“ฐ๋Š” ๊ฒƒ์ด ๊ณต์‹๋ฌธ์„œ์˜ ์˜ˆ์ œ๋กœ ๋‚˜์™€์žˆ์–ด์„œ ๊ทธ๊ฑธ ์ด์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. 

๋ฐฑ์—”๋“œ์— CORS ์„ค์ •๋„ ๋นผ๋จน์—ˆ์—ˆ๊ณ ์š”...

์ผ๋‹จ ํ™”๋ฉด์—์„œ ๋ฐ”๋€ ์ ์€ Blog > ๋‹ค์ด์–ด๋ฆฌ ๋ฉ”๋‰ด๋ฅผ ์„ ํƒํ•˜๋ฉด ๊ฒ€์ƒ‰์„ ์œ„ํ•œ ์ž…๋ ฅ์ฐฝ๊ณผ ๋ฒ„ํŠผ, ์ƒˆ๊ธ€ ๋“ฑ๋ก์„ ์œ„ํ•œ ๋ฒ„ํŠผ์„ ๋„ฃ์—ˆ๊ณ (๊ธฐ๋Šฅ์€ ์•„์ง ๊ตฌํ˜„๋ชปํ–ˆ๋„ค์š”), ๋ฆฌ์ŠคํŠธ ๋ชฉ๋ก ์กฐํšŒํ•˜๋Š” ๋ถ€๋ถ„๋งŒ ๊ฐ„๋‹จํ•˜๊ฒŒ ์—ฐ๊ฒฐํ•ด๋†“์•˜์Šต๋‹ˆ๋‹ค. ์†Œ์Šค์ฝ”๋“œ๋Š” ๋งŽ์ด ์ˆ˜์ •ํ–ˆ๋Š”๋ฐ ์‹ค์ œ ํ™”๋ฉด์—์„œ ๋ฐ”๋€๊ฑด ๋ญ ์—†๋„ค์š” ใ…กใ…กใ…‹

 

๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ ๊ธฐ๋Šฅ ๊ตฌํ˜„

 

์•ž์œผ๋กœ ํ•  ์ผ๋“ค์„ ๋‚˜์—ดํ•ด๋ดค์Šต๋‹ˆ๋‹ค.

  • ๋“ฑ๋ก๊ธฐ๋Šฅ ํ”„๋ก ํŠธ ๊ตฌํ˜„ ๋ฐ ๋ฐฑ์—”๋“œ์™€ ์—ฐ๋™
  • ๊ฒ€์ƒ‰๊ธฐ๋Šฅ ๋ฐฑ์—”๋“œ ๊ตฌํ˜„ ๋ฐ ํ”„๋ก ํŠธ์™€ ์—ฐ๋™
  • ๋ชฉ๋ก์˜ ํ—ค๋”์— ์ œ๋ชฉ, ๋‚ด์šฉ, ์ˆ˜์ •์ผ ํ‘œ์‹œ
  • ๋‚ด์šฉ์€ ํ•œ ์ค„์„ ๋„˜๊ธฐ์ง€ ์•Š๋„๋ก ์ผ๋ถ€๋งŒ ์ถœ๋ ฅ๋˜๋„๋ก ์ˆ˜์ •
  • ๋“ฑ๋ก์ผ์‹œ ํ‘œ์‹œ -> ๋ฐฑ์—”๋“œ ์ˆ˜์ •
  • ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ์ˆ˜์ •์ผ์‹œ๋„ ๊ด€๋ฆฌ
  • ๊ฒŒ์‹œ๊ธ€ ์ขŒ์ธก์— ๊ฒŒ์‹œ๊ธ€ ๋ฒˆํ˜ธ ๋„ฃ๊ธฐ -> ๋ชฝ๊ณ DB์— auto increment ๊ธฐ๋Šฅ์ด ์—†์–ด ์†Œ์Šค๋ ˆ๋ฒจ์—์„œ ๊ตฌํ˜„ํ•ด์•ผํ•จ

์—ญ์‹œ ํ”„๋ก ํŠธ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ํ˜ผ์ž ํ•˜๋ ค๋‹ˆ ํ•  ์ผ์ด ๋„ˆ๋ฌด ๋งŽ๋„ค์š” ใ…œใ…œ

๊ทธ๋ž˜๋„ ์žฌ๋ฏธ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด ๊ฐ€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์š”์ƒˆ ํ‹ฐ์Šคํ† ๋ฆฌ๊ฐ€ ์ž์ฒด๊ด‘๊ณ ๋ฅผ ๋ถ™์—ฌ์„œ ๊ด‘๊ณ ๊ฐ€ ์ž๊พธ ๋œจ๋Š”๊ฒƒ ๊ฐ™๋„ค์š”...๋กœ๊ทธ์ธํ•  ๋•Œ๋„ ๋œจ๊ณ ...

๋นจ๋ฆฌ ๋ธ”๋กœ๊ทธ ์•ฑ ๋งŒ๋“ค์–ด์„œ ๋ผ์ด๋ธŒํ•ด์„œ ์ง์ ‘ ์‚ฌ์šฉํ•ด๋ณด๊ณ  ์‹ถ๋„ค์š”

 

 

์ฐธ๊ณ ๋ฌธ์„œ:

์Šคํ”„๋ง๋ถ€ํŠธ CORS ์„ค์ •ํ•˜๊ธฐ >> spring.io/guides/gs/rest-service-cors/#global-cors-configuration

 

Enabling Cross Origin Requests for a RESTful Web Service

this guide is designed to get you productive as quickly as possible and using the latest Spring project releases and techniques as recommended by the Spring team

spring.io

๋ฆฌ์•กํŠธ ajax ํ˜ธ์ถœํ•˜๊ธฐ >> reactjs.org/docs/faq-ajax.html

 

AJAX and APIs – React

A JavaScript library for building user interfaces

reactjs.org

๋ฆฌ์•กํŠธ ๋ถ€๋ชจ์ƒํƒœ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ >> stackoverflow.com/questions/35537229/how-to-update-parents-state-in-react

 

How to update parent's state in React?

My structure looks as follows: Component 1 - |- Component 2 - - |- Component 4 - - - |- Component 5 Component 3 Component 3 should display some data depending on state of Component 5....

stackoverflow.com

 

์ž๋™์ƒ์„ฑ๋œ ์Šค์›จ๊ฑฐ API ๋ฌธ์„œ

์Šค์›จ๊ฑฐ๋Š” ๋ฐฑ์—”๋“œ์—์„œ ๊ฐœ๋ฐœํ•œ API๋ฅผ ๋ฌธ์„œํ™”ํ•ด์ฃผ๋Š” ํˆด๋กœ ๊ฐœ๋ฐœ ์ง„ํ–‰์ค‘์ผ ๋•Œ ๋ฟ๋งŒ์•„๋‹ˆ๋ผ ์šด์˜์ค‘์—๋„ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๊ธฐ๋„ ํ•˜๋Š”๋ฐ์š”

์Šค์›จ๊ฑฐ ๋ฒ„์ „์ด 3์œผ๋กœ ์˜ฌ๋ผ๊ฐ€๋ฉด์„œ ์„ค์ • ๋ฐ ๊ธฐ๋ณธ url์ด ๋ณ€๊ฒฝ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์Šค์›จ๊ฑฐ ๋ฒ„์ „ ์—…์— ๋‘์–ด๋ฒˆ ์‹คํŒจํ•œ ๋์— ๊ฐ€์žฅ ๊ฐ„๋‹จํžˆ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ๊ตฌํ•ด์™”์Šต๋‹ˆ๋‹ค.

 

์Šคํ”„๋ง๋ถ€ํŠธ ์Šค์›จ๊ฑฐ ์„ค์ •

๋ฒ„์ „์€ ์Šคํ”„๋ง๋ถ€ํŠธ 2.4.0, ์Šค์›จ๊ฑฐ 3.0.0 ์ž…๋‹ˆ๋‹ค.

 

1. ์Šคํ”„๋ง ๋ถ€ํŠธ์™€ ์Šค์›จ๊ฑฐ ์˜์กด์„ฑ ์ถ”๊ฐ€

๋ฉ”์ด๋ธํ”„๋กœ์ ํŠธ

<dependency>
	<groupId>io.springfox</groupId>
	<artifactId>springfox-boot-starter</artifactId>
	<version>3.0.0</version>
</dependency>
<dependency>
	<groupId>io.springfox</groupId>
	<artifactId>springfox-swagger-ui</artifactId>
	<version>3.0.0</version>
</dependency>

 

๊ทธ๋ž˜๋“ค ํ”„๋กœ์ ํŠธ

repositories {
	jcenter()
}

dependencies {
	implementation "io.springfox:springfox-boot-starter:3.0.0"
	implementation "io.springfox:springfox-swagger-ui:3.0.0"
}

 

2. ์„ค์ •ํ•˜๊ธฐ

SwaggerConfig ํด๋ž˜์Šค๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์„œ ์•„๋ž˜์™€ ๊ฐ™์ด @Configuration ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ์ฃผ๊ณ  Docket ๋นˆ์„ ํ•˜๋‚˜ ์ƒ์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ข€ ๋” ์ƒ์„ธํ•œ ์„ค์ •์„ ํ•˜๋ ค๋ฉด ๊ณต์‹์‚ฌ์ดํŠธ์—์„œ ํ™•์ธ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

@Configuration
public class SwaggerConfig {

	@Bean
	public Docket api() {
	return new Docket(DocumentationType.SWAGGER_2)
		.select()
		.apis(RequestHandlerSelectors.any())
		.paths(PathSelectors.any())
		.build();
	}
}

 

3. ์ ‘์† URL

๊ธฐ์กด์˜ 2.x ๋ฒ„์ „๊นŒ์ง€๋Š” host<:port>/<context root>/swagger-ui.html ๋กœ ์ ‘์†์„ ํ–ˆ์—ˆ๋‹ค๋ฉด, v3๋ถ€ํ„ฐ๋Š” host<:port>/<context root>/swagger-ui/ ๋˜๋Š” host<:port>/<context root>/swagger-ui/index.html ๋กœ ์ ‘์†์„ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

 

 

์ฐธ๊ณ ๋ฌธ์„œ: https://springfox.github.io/springfox/docs/current/

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Java + SpringBoot + Mongo DB ๋กœ ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

๊ฐ๊ฐ ์‚ฌ์šฉํ•  ๋ฒ„์ „์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

- Java 8 (์˜ค๋ผํดjdk๊ฐ€ ์ƒ์šฉ๋ชฉ์ ์œผ๋กœ๋Š” ์œ ๋ฃŒํ™”๋˜์–ด ์ถ”ํ›„ ์ฝ”ํ‹€๋ฆฐ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•ด๋ด์•ผ๊ฒ ๋„ค์š”)

- Springboot 2.4.0 (20๋…„ 11์›” ํ˜„์žฌ ์ตœ์‹ ๋ฒ„์ „)

- Mongo DB 4.4.2 Community Server (20๋…„ 11์›” ํ˜„์žฌ ์ตœ์‹ ๋ฒ„์ „)

 

๋ชฝ๊ณ ๋””๋น„ ์„ค์น˜ ๊ด€๋ จํ•ด์„œ๋Š” ๋ชฝ๊ณ DB ์ตœ์‹ ๋ฒ„์ „ ์„ค์น˜ํ•˜๊ธฐ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์„ค์น˜ ํ›„ ์‚ฌ์šฉ์ž ๊ณ„์ • ์ƒ์„ฑ ๋ฐ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์ƒ์„ฑ ๊ด€๋ จํ•ด์„œ๋Š” [๋ชฝ๊ณ DB] ๊ธฐ๋ณธ๋ช…๋ น์–ด ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

 

์ด์ œ ๊ทธ๋ž˜๋“ค ์ž๋ฐ” ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋‚˜ ์ƒ์„ฑํ•˜์—ฌ Blog > ๋‹ค์ด์–ด๋ฆฌ ๋ฉ”๋‰ด์—์„œ ์‚ฌ์šฉํ•  CRUD๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฐ€์žฅ ๋จผ์ € ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ์„ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ธ”๋กœ๊ทธ ๋งŒ๋“ค๊ธฐ ๋ฐฑ์—”๋“œ ํ”„๋กœ์ ํŠธ

 

์šฐ์„  build.graldeํŒŒ์ผ์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

plugins {
    id "org.springframework.boot" version "2.4.0"
    id 'java'
}

group 'com.keichee'
version '1.0-SNAPSHOT'
sourceCompatibility = "1.8"

repositories {
    jcenter()
}

ext {
    springVersion = '2.4.0'
}

dependencies {
    testImplementation group: 'junit', name: 'junit', version: '4.12'

    implementation 'org.mongodb:mongodb-driver-sync:4.1.1'
    testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'

    implementation "org.springframework.boot:spring-boot-starter-web:$springVersion"
    testImplementation("org.springframework.boot:spring-boot-starter-test:$springVersion") {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }

    compileOnly 'org.projectlombok:lombok:1.18.16'
    annotationProcessor 'org.projectlombok:lombok:1.18.16'

    testCompileOnly 'org.projectlombok:lombok:1.18.16'
    testAnnotationProcessor 'org.projectlombok:lombok:1.18.16'

    implementation "io.springfox:springfox-boot-starter:3.0.0"
    implementation 'io.springfox:springfox-swagger-ui:3.0.0'

    implementation 'com.google.guava:guava:30.0-jre'
}

test {
    useJUnitPlatform()
}

 

mongodb-driver ๋Š” sync 4.1.1 ์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๋กœ๊น…์„ ์œ„ํ•ด์„œ logback-classic์„ ์ถ”๊ฐ€ํ–ˆ๊ณ  

api ๋ฅผ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋„๋ก spring-boot-starter-web์„ ์ถ”๊ฐ€

getter, setter ๋“ฑ์˜ ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ์„ ์œ„ํ•ด lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๊ณ 

์Šค์›จ๊ฑฐ ๋ฌธ์„œ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  springfox ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๋„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์ด์ œ ๋ชฝ๊ณ DB์™€ ์—ฐ๊ฒฐ์„ ์œ„ํ•œ ์„ค์ •์„ ์•„๋ž˜์™€ ๊ฐ™์ด ํ•ด์ค๋‹ˆ๋‹ค.

@Configuration
public class MongoConfig {

    private static final String host = "localhost";
    private static final int port = 27017;
    private static final String database = "blogapp";

    @Bean
    public MongoDatabase blogAppDatabase() {
        MongoClient client = MongoClients.create(
                MongoClientSettings.builder()
                        .applyToClusterSettings(builder ->
                                builder.hosts(Collections.singletonList(new ServerAddress(host, port))))
                        .build());
        return client.getDatabase(database);
    }
}

 

์ปจํŠธ๋กค๋Ÿฌ์—๋Š” ๋”ฑ 4๊ฐœ์˜ Restful API๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

@Slf4j
@RestController
@RequestMapping("/diary/life")
@AllArgsConstructor
public class DiaryLifeController {

    private final DiaryLifeService diaryLifeService;

    @ApiOperation("์ „์ฒด ๋ชฉ๋ก ์กฐํšŒ")
    @GetMapping
    public Response<List<Post>> getPosts() {
        log.info("์ „์ฒด ๋ชฉ๋ก ์กฐํšŒ");

        return new Response<>(diaryLifeService.getPosts(null));
    }

    @ApiOperation("๋‹ค์ด์–ด๋ฆฌ ํฌ์ŠคํŒ… ์‹ ๊ทœ์ƒ์„ฑ")
    @PostMapping
    public Response<Void> savePost(@RequestBody Post post) {
        validatePostInput(post);
        diaryLifeService.createPost(post);
        return new Response<>();
    }

    @ApiOperation("๋‹ค์ด์–ด๋ฆฌ ํฌ์ŠคํŒ… ์—…๋ฐ์ดํŠธ")
    @PutMapping
    public Response<Void> updatePost(@RequestBody Post post) {
        validatePostInput(post);
        diaryLifeService.updatePost(post);
        return new Response<>();
    }

    @ApiOperation("๋‹ค์ด์–ด๋ฆฌ ํฌ์ŠคํŒ… ์‚ญ์ œ")
    @DeleteMapping
    public Response<Void> deletePost(@RequestParam String _id) {
        diaryLifeService.deletePost(_id);
        return new Response<>();
    }

    private void validatePostInput(Post post) {
        if (ObjectUtils.isEmpty(post.getTitle())) {
            throw new BadRequestException("No TITLE exists.");
        }
        if (ObjectUtils.isEmpty(post.getContent())) {
            throw new BadRequestException("No CONTENT exists.");
        }
    }

    @ExceptionHandler
    public ResponseEntity<Response<String>> badRequest(BadRequestException e) {
        log.error("Bad request.., e-msg:{}", e.getMessage(), e);
        return ResponseEntity.badRequest().body(new Response<>(e.getMessage()));
    }
}

 

์Šค์›จ๊ฑฐ๋กœ ๋ณด๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ๋‚˜์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์ด์–ด๋ฆฌ API ์Šค์›จ๊ฑฐ

 

์Šค์›จ๊ฑฐ๋ฅผ ์ด์šฉํ•ด์„œ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ํ•œ๋ฒˆ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

 

ํ˜„์žฌ ๊ตฌํ˜„๋œ ๊ธฐ๋Šฅ์€ ๋ชจ๋‘ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ ๊นŒ์ง€ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ๊นŒ์ง€ ์ž‘์—…ํ•˜๋ฉด์„œ ์‰ฝ๊ฒŒ ํ’€๋ฆฌ์ง€ ์•Š์•˜๋˜ ๋ถ€๋ถ„์€ ๋ชฝ๊ณ DB๋ฅผ ์ฒ˜์Œ ์‚ฌ์šฉํ•˜๋‹ค๋ณด๋‹ˆ ๋ชฝ๊ณ DB ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ด์šฉํ•œ CRUD๋งŒ๋“œ๋Š” ๋ถ€๋ถ„์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด RDS์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ObjectID ๋กœ ํ•ธ๋“ค๋ง์„ ํ•ด์•ผํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ์—ˆ๊ณ , auto increment pk ์„ค์ •์„ ๋”ฐ๋กœ ํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๊ฒŒ์‹œ๊ธ€์˜ ๋ฒˆํ˜ธ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด์•ผํ• ์ง€ ๊ณ ๋ฏผ์ด ์ข€ ๋˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ๊ฐ ๊ฒŒ์‹œ๊ธ€๋งˆ๋‹ค ์‹œํ€€์Šค๊ฐ’์„ ๋„ฃ์–ด์ค˜์•ผํ•˜๋Š”๋ฐ ๊ตฌ๊ธ€๋งํ•ด์„œ ๋‚˜์˜ค๋Š” ๊ฒƒ๋“ค์€ synchronized ๊ฐ€ ์•ˆ๋  ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์—ฌ์„œ ํ…Œ์ŠคํŠธ๋„ ์ข€ ํ•ด๋ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ๊นŒ์ง€ ๋ฐฑ์—”๋“œ์˜ ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ˜„์„ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋ก ํŠธ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ ์˜ฌ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.