๐Ÿšจ RIOT API๋ฅผ ์ด์šฉํ•ด์„œ ๋กค ์ „์  ๊ฒ€์ƒ‰ ์‚ฌ์ดํŠธ ๋งŒ๋“ค๊ธฐ (3) - DB ๊ตฌ์ถ• ๋ฐ ์ฝ”๋”ฉ

๊น€์ค€ํ˜ธยท2020๋…„ 6์›” 8์ผ
6
post-thumbnail

์ด๋ฒˆ ํฌ์ŠคํŠธ๋Š” ํ”„๋กœ์ ํŠธ ๋‘๋ฒˆ์งธ ์ด์Šˆ ํŒŒ์‹ฑํ•œ ๋ฐ์ดํ„ฐ๋กœ DB๊ตฌ์ถ•ํ•˜๊ธฐ์™€ ์ผ๋ถ€ ์„ธ๋ฒˆ์งธ ์ด์Šˆ๊ฐ€ ํฌํ•จ๋˜์–ด์žˆ๋‹ค. ๋‚˜๋Š” ๋ผ๋ฒจ์„ ํ†ตํ•ด ๋ฏธ๋ฆฌ ์ค€๋น„ํ•ด์•ผํ•˜๋Š” ๋ถ€๋ถ„์„ Preparations๋กœ ๊ฐœ๋ฐœ๋ถ€๋ถ„์„ feature๋กœ ๊ตฌ๋ถ„ํ•˜๊ณ ์žˆ๋‹ค.


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

๐Ÿšจ RESTFUL Project ๐Ÿšจ โ‡ข RIOT API + Spring Boot + AWS EC2 & RDS


AWS RDS (MySQL) ์ƒ์„ฑ

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

DB๋Š” MySQL 8.0.16 ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. RDS๋ฅผ ์ƒ์„ฑํ•˜๋ฉฐ ํ”„๋ฆฌํ‹ฐ์–ด ์„ค์ •์—์„œ ํฌ๊ฒŒ ๋ฐ”๊พผ๊ฒƒ์€ ์—†๊ณ , ๋‹จ์ˆœํžˆ ์ธ์ฝ”๋”ฉ์€ utf-8๋กœ ๋ฐ”๊พธ๊ณ  public access ํ—ˆ์šฉ๊ณผ ๋ณด์•ˆ์‚ฌํ•ญ์— ๋Œ€ํ•ด์„œ๋งŒ ์ˆ˜์ •ํ–ˆ๋‹ค.

DB ์„ค๊ณ„

์‚ฌ์‹ค DB๋ฅผ ์„ค๊ณ„ํ•˜๊ธฐ์—๋Š” ์ง€์‹์ด ๋งŽ์ด ๋ถ€์กฑํ•˜๊ณ  ๊ฒฝํ—˜๋„ ๋ถ€์กฑํ•˜๋‹ค. ๊ทธ๋ž˜์„œ DB๋ฅผ ์„ค๊ณ„ํ•˜๊ธฐ์— ์•ž์„œ์„œ ๋‹ค์Œ๊ณผ๊ฐ™์€ ์˜๋ฌธ์ ์ด ๋“ ๋‹ค.

  1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ •๋ณด ์š”์ฒญ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ RIOT API์—์„œ ๋‹ค์ด๋ ‰ํŠธ๋กœ ์ œ๊ณตํ•ด์ค˜๋„ ๊ดœ์ฐฎ์„๊นŒ?

  2. ๋‹ค์ด๋ ‰ํŠธ ์ œ๊ณต์ด ๋งž๋‹ค๊ณ ํ•ด๋„ MVC๊ตฌ์กฐ ์—ฐ์Šต์„์œ„ํ•ด DB๋ฅผ ์‚ฌ์šฉํ•˜๊ณ ์žํ•˜๋Š”๋ฐ, ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด๋†“๋Š”๊ฒƒ์ด ํ•ฉ๋ฆฌ์ ์ผ๊นŒ?

๊ทธ๋ž˜์„œ OKKY์— ์งˆ๋ฌธ์„ ๋™์ผํ•˜๊ฒŒ ์˜ฌ๋ ธ๋‹ค. ๋ฏผ๊ฐํ•œ ๋Œ€๋‹ต์ธ์ง€ ๋‚ด๊ฐ€ ๋„ˆ๋ฌด ๋‘์„œ์—†์ด ๋ฌผ์–ด๋ณธ๊ฑด์ง€ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ๋‹ต๊ธ€์ด ํ•˜๋‚˜๋„ ์•ˆ๋‹ฌ๋ ธ๋‹ค....

์ผ๋‹จ ์ œ์ณ๋‘๊ณ  ๋‚˜๋ฆ„๋Œ€๋กœ์˜ ๋…ผ๋ฆฌ๋ฅผ๊ฐ€์ง€๊ณ  DB๋ฅผ ์•ฝ์‹์œผ๋กœ ๊ตฌ์„ฑํ•˜๊ธฐ๋กœํ–ˆ๋‹ค. ์ผ๋‹จ ์ฒซ๋ฒˆ์งธ๋กœ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ค„๊ฒƒ์ธ๊ฐ€๋ฅผ ์ƒ๊ฐํ•ด๋ณด์•˜๋‹ค.

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

  • ์†Œํ™˜์‚ฌ ์•„์ด์ฝ˜ + ๋‹‰๋„ค์ž„
  • ์†Œํ™˜์‚ฌ ํ‹ฐ์–ด + ์•„์ด์ฝ˜
  • ์†Œํ™˜์‚ฌ ํ˜„์žฌ ๊ฒŒ์ž„ ์ •๋ณด
  • ์†Œํ™˜์‚ฌ ์ „์  ๋ฆฌ์ŠคํŠธ
  • ์†Œํ™˜์‚ฌ ์ฑ”ํ”ผ์–ธ ์ˆ™๋ จ๋„ ์ •๋ณด

๊ทธ๋Ÿฐ๋ฐ ์‚ฌ์‹ค ์†Œํ™˜์‚ฌ ๋‹‰๋„ค์ž„์„ํฌํ•จํ•ด์„œ ์œ„์— ๋‚˜์—ดํ•œ ๋ชจ๋“ ๊ฒƒ์ด ์–ธ์ œ๋“ ์ง€ ๋ณ€ํ• ์ˆ˜์žˆ๋Š” ๋‚ด์šฉ์ด๋ผ์„œ, ์ฆ‰ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์ ์šฉ์ด ๋˜์–ด์•ผํ•˜๋ฏ€๋กœ DB์— ์ €์žฅํ•˜๋Š”๊ฒƒ๋ณด๋‹ค API์—์„œ ๊ฐ€์ ธ์˜ค๋Š”๊ฒƒ์ด ํ•ฉ๋ฆฌ์ ์ด๋ผ๊ณ  ๋Š๊ผˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์–ธ์ œ๊นŒ์ง€๋‚˜ ์—ฐ์Šต์— ํฌ์ปค์‹ฑ์„ ํ–ˆ๊ธฐ๋•Œ๋ฌธ์— ์ ์–ด๋„ ํ•œ๊ฐ€์ง€ ํ…Œ์ด๋ธ”์— ๋Œ€ํ•ด์„œ๋Š” ๋งŒ๋“ค๊ธฐ๋กœํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์†Œํ™˜์‚ฌ ๋‹‰๋„ค์ž„์„ ํ†ตํ•ด์„œ API๋กœ ๋ถ€ํ„ฐ ์–ป์–ด์˜จ ์†Œํ™˜์‚ฌ ์ •๋ณด๋ฅผ DB์— ๊ตฌ์ถ•ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. ๋กœ์ง์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. DB์— ์•”ํ˜ธํ™”๋œ ๊ณ„์ • ID๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฐ€?
  2. ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด RIOT API๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์™€์„œ ์ €์žฅํ•˜๊ธฐ
  3. ๋งŒ์•ฝ ๊ณ„์ • ๋‹‰๋„ค์ž„์ด ๋ณ€๊ฒฝ๋˜์–ด์„œ ์ •์ƒ์ ์œผ๋กœ ๊ฒ€์ƒ‰์ด ์•ˆ๋ ๊ฒฝ์šฐ, ์ €์žฅ๋œ ์•”ํ˜ธํ™”๋œ ๊ณ„์ • ID๋ฅผ ํ†ตํ•ด์„œ ๋ณ€๊ฒฝ๋œ ๋‹‰๋„ค์ž„์„ ํŒŒ์•…ํ•˜์—ฌ DB์— update ์‹œํ‚ค๊ธฐ

์ด๋ ‡๊ฒŒ ์‚ฌ์šฉ์ž ์ •๋ณด์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋งŒ DB์— ์ผ๋‹จ ๊ตฌ์ถ•ํ•ด๋†“๊ณ  ๋‚˜์ค‘์— ํ•„์š”์‹œ ์ถ”๊ฐ€ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

์ฝ”๋”ฉ (MVC)

๋จผ์ € Spring boot๋ฅผ ์ด์šฉํ•ด์„œ RESTํ˜•์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฟŒ๋ฆด๊ฒƒ์ด๊ธฐ๋•Œ๋ฌธ์— RestController ๋ฐ‘์—์„œ ์ž‘์—…ํ–ˆ๋‹ค. ๋˜ํ•œ maven 2.27 ๋ฒ„์ „, jdk 1.8์—์„œ ์ง„ํ–‰ํ–ˆ์œผ๋ฉฐ, ์ถ”๊ฐ€์ ์ธ dependency๋Š” ๊ธฐ๋ณธ jackson๊ณผ ์ถ”๊ฐ€์ ์ธ mybatis, junit๋“ฑ์„ ๋„ฃ์–ด์„œ ์ง„ํ–‰ํ–ˆ๋‹ค.

1. findByName

๋จผ์ € DB์— ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญํ•œ ์†Œํ™˜์‚ฌ ์ •๋ณด๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

controller

@GetMapping(value = "/summoner/{name}")
public ResponseEntity<Summoner> summoner(@PathVariable String name) {
    Summoner summoner = new Summoner();

    summoner = summonerService.findByName(name);	// ์†Œํ™˜์‚ฌ ์ •๋ณด ์กฐํšŒ
    if(summoner != null && !summoner.equals(null)) {
    	return new ResponseEntity<Summoner>(summoner,HttpStatus.OK);
    }
    return new ResponseEntity<Summoner>(HttpStatus.NO_CONTENT);
}

Summoner ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์„œ ์†Œํ™˜์‚ฌ ์ด๋ฆ„์„ ํ†ตํ•ด ์†Œํ™˜์‚ฌ ์ •๋ณด๋ฅผ ์–ป์–ด์˜ค๊ฒŒ ํ–ˆ๋‹ค. ์ดํ›„์˜ ๋กœ์ง์—๋Š” ํฐ ๋‹ค๋ฅธ ์ ์ด ์—†์–ด์„œ ์ƒ๋žตํ•˜๊ณ , SummonerMapper.xml์—์„œ ์ž‘์„ฑํ•œ ์ฟผ๋ฆฌ๋ฌธ์„ ํ™•์ธํ•ด๋ณด์ž.

mapper

<mapper namespace="com.junho.mapper.SummonerMapper">

  <select id="findByName" resultType="com.junho.dto.Summoner">
    SELECT * FROM USR_INFO WHERE NAME = #{name};
  </select>
  
</mapper>

๊ฐ„๋‹จํ•˜๊ฒŒ SELECT๋ฌธ์„ ํ†ตํ•ด์„œ ์ •๋ณด๋ฅผ ์–ป์–ด์™”๋‹ค. id๋Š” SummonerMapper.java์˜ ๋ฉ”์„œ๋“œ ์ด๋ฆ„๊ณผ ๋งž์ถฐ์ฃผ์—ˆ๊ณ , resultType์€ Summoner์— ๋Œ€ํ•œ DTO๋กœ ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.

์œ„์—์„œ ์–ธ๊ธ‰ํ•œ๋ฐ”์™€๊ฐ™์ด ์ฒ˜์Œ๋ถ€ํ„ฐ DB์— ๋„ฃ์–ด๋†“์ง€ ์•Š์„ ์ƒ๊ฐ์ด๋‹ค. ๋”ฐ๋ผ์„œ DB์— ์—†์œผ๋ฉด ์ฃผ์ž…ํ•ด์ค˜์•ผํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

2. insertSummoner

controller

summoner = summonerService.findByName(name);	// ์†Œํ™˜์‚ฌ ์ •๋ณด ์กฐํšŒ
try {	
	System.out.println(summoner.getName() + " : DB์—์„œ ์ฐพ์Œ");
	} catch(Exception e) { 	// DB์— ์—†๋‹ค๋ฉด API์—์„œ ์†Œํ™˜์‚ฌ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
		SummonerParser summonerParser = new SummonerParser();
		summoner = summonerParser.getJsonData(name);
		summonerService.insertSummoner(summoner);	// ์†Œํ™˜์‚ฌ ์ •๋ณด DB์— ์ถ”๊ฐ€
	}
   }

findByName ์•„๋ž˜์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ํ–ˆ๋‹ค. ๋จผ์ € DB์— ์ฐพ๊ณ ์žํ•˜๋Š” ์ •๋ณด๊ฐ€ ์กด์žฌํ•˜์ง€์•Š๋Š”๋‹ค๋ฉด null Point Execption์ด ๋ฐœ์ƒํ•˜๋Š”๋ฐ, ์ด๊ฒƒ์„ '์ž˜' ์˜ˆ์™ธ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ชฐ๋ผ์„œ ์œ„์™€๊ฐ™์ด ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•ด์„œ summoner ์ •๋ณด๋ฅผ RIOT API๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์™”๋‹ค. (์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒํ•ด์•ผ ์ž˜ํ–ˆ๋‹ค๊ณ  ์†Œ๋ฌธ๋‚ ๊นŒ์š”?)

๊ทธ๋ฆฌ๊ณ  DB์— ๋‹ค์‹œ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๋กœ์ง์„ ์œ„ํ•ด์„œ insertSummoner๋ฅผ ํ˜ธ์ถœํ–ˆ๋‹ค.

mapper

<insert id="insertSummoner" parameterType="com.junho.dto.Summoner">
  INSERT INTO USR_INFO VALUES(
  	#{accountId},
  	#{profileIconId},
  	#{revisionDate},
  	#{name},
  	#{id},
  	#{puuid},
	#{summonerLevel})
</insert>

findByName๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐ™์€ ์…‹ํŒ…์„ ํ•˜์˜€๊ณ , ๋‹ค๋ฅธ์ ์€ resultType์ด ์—†๋‹ค๋Š” ์ ๊ณผ parameter๋กœ ๋„ฃ์–ด์ค€ Summoner ๊ฐ์ฒด๋ฅผ ์ธ์‹ํ•˜๊ธฐ์œ„ํ•ด์„œ parameterTpye์„ Summoner DTO๋กœ ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.

์ •๋ฆฌ

์ด๋ฒˆ์—๋Š” AWS RDS๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ…Œ์ด๋ธ”์„ค๊ณ„๋ฅผ ํ•ด๋ณด๋ ค๊ณ ํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฌธ์ œ๋Š” ์‹ค์‹œ๊ฐ„์ด๋ผ๋Š” ์ ์—์„œ ํ˜ผ๋ž€์ด ์™”๋‹ค. ๊ฒŒ์ž„์˜ ์†Œํ™˜์‚ฌ ์•„์ด๋”” ์ •๋ณด๋„ ๋งค๋ฒˆ ๊ฐฑ์‹  ๋ฐ ์ˆ˜์ •์ด ๋ ํ…Œ๊ณ , ์ „์ ๋ฆฌ์ŠคํŠธ, ์ˆ™๋ จ๋„๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐฑ์‹ ์ด ๋ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์ •๋ณด๋ฅผ RIOT API๋ฅผ ์˜์กดํ•ด์•ผ๋งŒ ํ• ๊นŒ? ์—ฌ๊ธฐ์„œ ๋‚ด๊ฐ€ ์ƒ๊ฐํ•œ ๋ฐฉํ–ฅ์€ ์ด๋ ‡๋‹ค.

  1. ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜จ๋‹ค
  2. DB์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ RIOT API์— ๋“ค์–ด๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ๋’ค, DB์— ์ฃผ์ž…ํ•œ๋‹ค.
  3. ์• ์ดˆ์— DB์— ๋“ค์–ด๊ฐ€๋Š” ๋ฐ์ดํ„ฐ๋Š” ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ด์ฃผ๋Š” ์‚ฌ๋žŒ์˜ ๊ฐ€๊ณต๋œ ๋ฐ์ดํ„ฐ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ 1๋ฒˆ์€ ๋น„๋Š” ์‹œ๊ฐ„์— ์ •ํ™•ํ•œ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ์ ์—์„œ ์• ๋Ÿฌ์‚ฌํ•ญ์ด ์žˆ๊ธฐ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅ์„ฑ์ด ๋‚ฎ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  3๋ฒˆ ๊ฐ™์€๊ฒฝ์šฐ ์œ ์˜๋ฏธํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค๋‹จ๊ณ„๋Š” ์•„๋‹ˆ๋ผ๊ณ  ์ƒ๊ฐํ•ด์„œ 2๋ฒˆ์„ ์ฑ„ํƒํ•ด์„œ ์ง„ํ–‰ํ–ˆ๋‹ค.

๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์—ฌ์ „ํžˆ RIOT API์—์„œ ๋‹ค์ด๋ ‰ํŠธ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š” ๊ฒƒ์ด ์ •๋‹ต์ด๋ผ๋Š” ๋Š๋‚Œ์„ ๋ฐ›๋Š”๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ MVCํŒจํ„ด ๊ณต๋ถ€๋ฅผ ์œ„ํ•ด์„œ๋ผ๋„ DB๋ฅผ ๊ตฌ์ถ•ํ•˜๊ธฐ๋กœ ํ–ˆ๊ธฐ๋•Œ๋ฌธ์— 2๋ฒˆ์„ ๋ฐ€๊ณ  ๋‚˜๊ฐ”๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜ ํ•œ๋ฒˆ ์ง€์‹์˜ ํ•œ๊ณ„๋ฅผ ๋Š๋ผ๋ฉฐ ๋” ๊ณต๋ถ€ํ•ด์•ผ๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์„ ๊ฐ€์ง€๋ฉฐ 3ํŽธ์„ ๋งˆ๋ฌด๋ฆฌํ•œ๋‹ค.

profile
https://junhok82.github.io/

1๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2020๋…„ 6์›” 8์ผ

Great

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ