git ์ ๋ถ์ฐ ๋ฒ์ ๊ด๋ฆฌ ์์คํ
์ค ๊ฐ์ฅ ์ ์๋ ค์ ธ ์๋ค๊ณ ํด๋ ๊ณผ์ธ์ด ์๋ ์ ๋๋ก ๋๋ถ๋ถ์ ์์คํ
์์ ์ฌ์ฉ๋๊ณ ์๋ ๊ฒ ๊ฐ๋ค. ์ด๋ฅผ ์น์๋น์ค์์ ๋ณด๋ค ํธํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ํ ์์คํ
์ด Github. Github ์ ์ฌ์ฉํ๋ ์ด์ ์ค์ ๊ฐ์ฅ ํฐ ์ด์ ๋ฅผ ํ๋๋ง ์ด์ผ๊ธฐํด๋ณด์๋ฉด ๋ฐ๋ก ์จ๋ผ์ธ์์์ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํ ์ ์๋ pullRequest๋ผ๋ ๊ธฐ๋ฅ ๋๋ฌธ์ด ์๋๊น ์กฐ์ฌ์ค๋ฝ๊ฒ ์๊ฐ์ ํด๋ณธ๋ค.
ใpullRequest๋ work branch์์ ์์
ํ ๋ด์ฉ์ base branch๋ก merge ์ ๊ผญ ์ฝ๋ ๋ฆฌ๋ทฐ๊ฐ ์๋๋๋ผ๋ ์์
ํ ๋ด์ฉ์ ๋ํด์ ๋ค์ํ ๊ฒ์ฌ๋ฅผ ์๋ํํ ์ ์๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ด ๋ง๋ค. ์ด๋ฌํ ์๋ํ๋ CI(์ง์์ ํตํฉ) ๊ด์ ์์ ๋งค์ฐ ์ค์ํ๋ฐ ์ฝ๋์ ๋ํด ์ฒดํฌํด์ผ ํ ๋ถ๋ถ๋ค(๋น๋, ํ
์คํธ, ์ ์ ๋ถ์ ๋ฑ)์ “์์์” ํด์ค๋ค๋ฉด ์์
์๋ ์ค๋กฏ์ด ๋น์ฆ๋์ค ๋ก์ง ๊ฐ๋ฐ์ ๋ํด์๋ง ์ ๊ฒฝ ์ธ ์ ์์ผ๋ ์์ฐ์ฑ ์ ์ฝ ์ธก๋ฉด์์ ์์ฒญ๋ ํจ๊ณผ๋ฅผ ๋ณผ ์ ์๋ค.
๋ด๊ฐ ํ๋์ผ์๋ง ์ง์คํ ์ ์๊ฒ! ์ถ์ฒ : https://www.clien.net/service/board/park/10453442" ๋ด๊ฐ ํ๋์ผ์๋ง ์ง์คํ ์ ์๊ฒ! ์ถ์ฒ : https://www.clien.net/service/board/park/10453442 ์ด๋ฒ ํฌ์คํ
์์๋ ๊ทธ์ค์์๋ ์์ฃผ ๊ฐ๋จํ ์ค์ ๋ง์ผ๋ก work branch์ ๋น๋ ์ํ๋ฅผ ๊ฒ์ฌํด ๋ณผ ์ ์๋ Jenkins์ Github Pull Request Builder๋ฅผ ์ค์น ๋ฐ ํ์ฉํด ๋ณด๊ณ ์ ํ๋ค.
์ฌ์ค ์ต๊ทผ ํ์์ CI ์๋ฒ๋ฅผ ์ด์ ํด์ผ ํ์๋ค. ๋จธ๋ฆฟ์์์๋ ์ด๋ป๊ฒ ํ๋ฉด ๋๊ฒ ์ง ์ถ์์ง๋ง ๋ง์ ํด๋ณด๋ ค๋ Jenkins ๋ฒ์ ์
๋ ๋์๊ณ ๋ญ๋ถํฐ ํด์ผ ํ ์ง ํ๋ฅ๋๋ ํ์๊ฐ ๋ถ๋๋ฌ์ ๋ค. ์ด์ฐธ์ ์ ๋ฆฌ๋ฅผ ํด๋ณด๋ฉฐ ๋ค์ ํ๋ฒ ๋ฆฌ๋ง์ธ๋ ํ๋ ์๊ฐ์ ๊ฐ์ ธ๋ณด๊ณ ์ ํ๋ค. (์ด๋์ ๊ธฐ์ต๋ณด๋ค ๊ธฐ๋ก์ด ์ค์ํ๋ค.)
์ค๋น๋ฌผ ใ์ ์ฒด์ ์ธ ํ๋ฆ์ ์๋ ๊ทธ๋ฆผ์ฒ๋ผ ํ๋ฌ๊ฐ๊ธฐ ๋๋ฌธ์ ๋น์ฐํ ์๋ฒ์ Jenkins ๊ฐ ์ค์น๋์ด ์์ด์ผ ํ๋ค. Jenkins ์ค์น๋ ํ์์ ํฌ์คํ
(Jenkins ์ค์น ์นํธํค)๋ฅผ ์ฐธ๊ณ ํด ๋ณด๋ ๊ฒ๋ ์ข์ ๊ฒ ๊ฐ๋ค.
์ ์ฒด์ ์ธ ํ๋ฆ" ์ ์ฒด์ ์ธ ํ๋ฆ ใ์ฐธ๊ณ ๋ก ํ์๋ GitHub Enterprise ๋ฒ์ ์์ ์ฌ์ฉํ๋๋ฐ ์ผ๋ฐ Github์์๋ ๋์ผํ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
Github๊ณผ Jenkins์ ์ฐ๋์ ์ํ 2๊ฐ์ง ์ค์ ใGithub ๊ณผ Jenkins ๊ฐ ํต์ ์ด ๋๋๋ก ์ค์ ํด ์ค์ผ ํ๋ค. ๊ทธ๋์ผ Github์ ์ฝ๋๋ฅผ ๋ฐ์์ Jenkins ๊ฐ ๋น๋๋ฅผ ํ๊ณ ๊ทธ ๋น๋ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ Github์ ๋ฆฌํฌํธ๊ฐ ๊ฐ๋ฅํด์ง๊ธฐ ๋๋ฌธ์ด๋ค. ๋จผ์ ์ฒซ ๋ฒ์งธ๋ก ssh ์ค์ ์ผ๋ก Github์ ์ฝ๋๋ฅผ ๊ฐ์ ธ์ค๋๋ก ssh ์ค์ ์ ํด๋์. ssh ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ํ์์ ํฌ์คํ
(Github๊ณผ Jenkins ์ฐ๋ํ๊ธฐ)ํธ์ ํ์ธํด๋ณด๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
ใ๊ทธ๋ค์์ผ๋ก ์๋์์ ์ด์ผ๊ธฐํ GitHub Pull Request Builder๋ผ๋ Jenkins plugin ์ด ๋น๋๊ฐ ๋๋ ๋ค์ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํฌํ
ํด์ค ์ ์๋ ์ธ์ฆ ํ ํฐ์ ๋ฐ๊ธ๋ฐ์๋์. Github > Settings > Developer settings > Personal access tokens ํ๋ฉด์์ ํค๋ฅผ ์์ฑํ๊ณ ๋ง๋ค์ด์ง ํค๋ฅผ ์ ์ฅํด ๋๋ค. (์ด ํค๋ ๋ณด์์ ์ ์ํด์ผ ํ๊ณ , ํ๋ฉด ๊ฒฝ๊ณ (?)์์๋ ๋ณผ ์ ์๋ฏ์ด ํค๋ ์์ฑ ์ ํ ๋ฒ๋ฐ์ ๋ณผ ์ ์๊ธฐ ๋๋ฌธ์ ๋ฏธ๋ฆฌ ์ ์ฅํด ๋ฌ์ผ ํ๋ค.)
์ธ์ฆํ ํฐ์ ๋ฏธ๋ฆฌ ๋ฐ์๋์." ์ธ์ฆํ ํฐ์ ๋ฏธ๋ฆฌ ๋ฐ์๋์. Jenkins ์ค์ ใJenkins > ๊ด๋ฆฌ > pluginManager์ ๋ค์ด๊ฐ GitHub Pull Request Builder๋ฅผ ๊ฒ์ ํ ์ค์นํด ์ค๋ค. ๊ทธ๋ฌ๊ณ ๋์ Jenkins > ๊ด๋ฆฌ > ํ๊ฒฝ์ค์ ์ ๋ค์ด๊ฐ ๋ณด๋ฉด ์๋์ ๊ฐ์ด GitHub Pull Request Builder ํญ๋ชฉ์ด ์๊ธด ๊ฒ์ ํ์ธํ ์ ์๊ณ ์์์ ์ค์ ํ ์ธ์ฆํ ํฐ์ ์๋์ฒ๋ผ ๋ฑ๋ก ํ ์ ์ฅ์ ํ๋ค.
credentials ์ ์์์ ๋ฐ๊ธ๋ฐ์ ์ธ์ฆํ ํฐ์ผ๋ก ๋ฑ๋กํด์ค๋ค." credentials ์ ์์์ ๋ฐ๊ธ๋ฐ์ ์ธ์ฆํ ํฐ์ผ๋ก ๋ฑ๋กํด์ค๋ค. ใJenkins job์ ํ๋ ๋ง๋ค๊ณ pullRequest ๊ฐ ๋ฐ์ํ์ ๋ ์๋์ผ๋ก ์คํ๋ ์ ์๋๋ก ์ค์ ์ ํด์ค๋ค. ๋จผ์ General ํญ์ Github project์ Github url ์ ์ ์ด์ฃผ๊ณ
ใ์์ค ์ฝ๋ ๊ด๋ฆฌ ํญ์์ ssh ์ฃผ์๋ฅผ ์ ๊ณ ์์์ ๋ฏธ๋ฆฌ ์ค์ ํ ssh ํค๋ก credentials ๊ฐ์ ๋ฃ์ด์ค๋ค. ์ ์๋ ์ด์ผ๊ธฐํ์ง๋ง ์ด ๋ถ๋ถ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ๋นจ๊ฐ์ ๊ธ์จ๋ก ์ค๋ฅ ๋ด์ฉ์ด ๋์ค๊ณ ์๋ ํ๋ฉด์ฒ๋ผ ์ค๋ฅ๊ฐ ์๋ค๋ฉด ์๋ฌด๊ฒ๋ ์ ๋์จ๋ค. Refspec ์ +refs/pull/*:refs/remotes/origin/pr/* ๋ผ๊ณ ์ ์ด์ฃผ๊ณ ๋ธ๋์น ์ค์ ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์์์ pullRequest๋ฅผ ๋ฐ์์ํจ ๋ธ๋์น๋ฅผ ๋น๋ ํ ์ ์๋๋ก ${sha1} ๋ผ๊ณ ์ ์ด์ฃผ์.
์ ํํ 2018๋
07์ 12์ผ ํ์์ ์ฒซ ํ ์ด ํ๋ก์ ํธ์ธ โ๊ธฐ์ ๋ธ๋ก๊ทธ ๊ตฌ๋
์๋น์คโ๋ฅผ ์คํํ๊ฒ ๋๋ค. ์ผ๋ง๋ ๋ง์ด ๊ตฌ๋
(๊ฐ์
) ํ๊ฒ ์ด ํ๋ ์๊ฐ์ด ๋ถ๋๋ฌ์ธ ๋งํผ 6๊ฐ์์ด ์ง๋ ๊ตฌ๋
์ ์๋ 1,000๋ช
์ ๋๊ธฐ๊ณ 1๋
์ด ์ง๋ 2,000๋ช
.์ด๋๋ง ๋ฌ๋ ฅ์ ๋ณด๋ ์ค๋์ด ์ ํํ๊ฒ ํ ์ด ํ๋ก์ ํธ๋ฅผ ์๋น์คํ์ง ๋ฒ์จ 2๋
์ด ๋๋ ๋ . ๊ตฌ๋
์ ์๋ ์ด๋๋ง 3,000๋ช
์ ๋์ด์ ๋ค. ๋ญ๊ฐ ๋ฟ๋ฏํ๋ฉด์๋ ์๋น์ค๋ฅผ ์ข ๋ ๋๋ฒจ๋กญ ํ์ง ๋ชปํ ํ์ ์์ ์ ๋์๋ณด๋ ๊ดํ ๋ง์์ด ๋ฌด๊ฑฐ์์ง๊ณ .
๋ญ๊ฐ ํด์ผํ๋๋ฐ… ๊ดํ ๋์น๋ง ๋ณด์ด๋ค…์ถ์ฒ : http://egloos.zum.com/nievess/v/657827" ๋ญ๊ฐ ํด์ผํ๋๋ฐ… ๊ดํ ๋์น๋ง ๋ณด์ด๋ค…
์ถ์ฒ : http://egloos.zum.com/nievess/v/657827 ใ์ง๋ 2๋
๋์์ ๋์ด์ผ๋ณด๋ฉฐ ์๋น์ค๋ฅผ ์ด๋ป๊ฒ ์ด์ํด ์๋์ง, ๊ทธ๋ฆฌ๊ณ ํ ์ด ํ๋ก์ ํธ๊ฐ ํ์์๊ฒ ์ด๋ค ์ํฅ์ ์ฃผ์๋์ง ๋๋์๋ณด๋ฉฐ ์
ํ ๋ฆฌ๋ทฐ๋ฅผ ํด ๋ณด๊ณ ์ ํ๋ค.
์๋น์ค ์์ฒด ํ๊ฐ ์ฌํํ ๊ธฐ๋ฅ ใ๋ง ๊ทธ๋๋ก ํ ์ด ํ๋ก์ ํธ์ด๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ฅ ๋ํ ์์ฃผ ๊ฐ๋จํ๋ค. awesome-devblog์์ ์ ๊ณตํ๋ ๊ฐ์ธ/๋จ์ฒด ๋ธ๋ก๊ทธ๋ค์ ํฌ์คํ
์ ์กฐํํ์ฌ ์ด์ ์์ฑ๋ ๊ธ๋ค๋ง ๋ชจ์ ๋ฐ์กํ๋ค. ๊ฑฐ๊ธฐ์ ์ฃผ๊ฐ ๋ง์ด ํด๋ฆญ๋ ํฌ์คํ
์ ๋ชจ์์ ํ ๋ฒ ๋ ๋ฐ์กํ๋ ๊ธฐ๋ฅ๊น์ง. ์ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ๋ ๋๋ฒจ๋กญ ํด์ผ ํ๋๋ฐ ์์ด๋์ด๊ฐ ์์ด์ ์ธ์ง ๋๋ฒจ๋กญ ํ ํ์ด ์ ๋์ ์ธ์ง ์ ์ง๋ง ํ๊ณ ์๋ ์ํ๋ค.
์๋น์ค์ ์์ด์๋ ์๋ ‘๋ก๊น
(Logging)’ ใํ์์ ๋ง๋ก ํ๊ณ ์ปดํจํฐ๋ก ๋์๊ฐ๋ ๋ชจ๋ ‘ํ๋ก๊ทธ๋จ’์ ์ํฉ์ ๋ฐ๋ผ ๋ฏธ๋ฆฌ ๋ง๋ค์ด ๋์ ๋ก์ง์ ๋ฐ๋ผ ์์ง์ด๋ ๋ก๋ด์ ๋ถ๊ณผํ๋ค. ๋ฌผ๋ก ์์ฆ์๋ ๋จธ์ ๋ฌ๋์ด๋ AI ๊ฐ์ ๊ธฐ์ ๋ค๋ก ์ปดํจํฐ๊ฐ ์ค์ค๋ก ํ์ตํ๋ ๊ฒฝ์ฐ๋ ์์ง๋ง ๊ทธ ๋ํ ๋ฏธ๋ฆฌ ์ฝ๋ฉ์ ํตํด ๋ง๋ค์ด์ง ๋ถ๋ถ๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ 2๋
์ด ์ง๋ ์ง๊ธ ์ด์ ๊น์ง ์๋น์ค๊ฐ ์ด๋ป๊ฒ ๋์๊ฐ๋์ง๋ฅผ ํ์ธํ๊ธฐ ์ํด์๋ ์ฌ์ ์ ์ค๋นํด์ผ ํ ๊ฒ์ด ์๋ค. ๊ทธ๊ฒ์ ๋ฐ๋ก ‘๋ก๊น
’. ์๋น์ค ํฌ์
์ ๋ถํฐ ํ๋ก ํธ๋ถํฐ ๋ฐฑ์๋๊น์ง ๋ค์ํ ๋ก๊น
์ ํด์์ธ์ง 2๋
์ด ์ง๋ ์ง๊ธ, ๊ธฐ๋ก๋ ๋ก๊ทธ๋ก ๋ค์ํ ์๋น์ค ์งํ๋ฅผ ํ์ธํด ๋ณผ ์ ์์์ ๋คํ์ด๋ผ ์๊ฐํ๋ค.
๊ฐ์ข
์งํ ใ๋จผ์ ๋ด์ผ ํ ์งํ๋ ๋น์ฐํ ๊ฐ์
/ํด์ง ์ถ์ด. ๋๋ผ๋งํฑ ํ ์ ํ ๊ทธ๋ํ๋ ์๋์ง๋ง ๋น์ฐํ(?) ํด์ง ๋ณด๋ค ๊ฐ์
์ด ๋ ๋ง๊ณ ์๊ฐ์ด ์ง๋ ์๋ก ์ด๋ ์ ๋ ๊พธ์คํ๊ฒ ๊ฐ์
์๊ฐ ๋ค์ด์ค๋ ๊ฒ์ ๋ณด๋ฉด ์ด๋ป๊ฒ ์๊ณ ๊ฐ์
์ ํ๋ฌ ์ค๋์ง ์ ๊ธฐํ ๋ฐ๋ฆ์ด๋ค. ํ์ง๋ง ๋ง๋ฅ ์ ๊ธฐํดํ์ง๋ง ๋ง๊ณ ํด์งํ๋ ์์ธ์ ๋ถ์ํด์ผ ํ ํ์๊ฐ ์์ด ๋ณด์ธ๋ค. ์๋ง๋ ์์งํ๋ ๋ธ๋ก๊ทธ๋ค ์ค ๊ฐํน ๊ฐ๋ฐ๊ณผ ๊ด๋ จ๋์ง ์๋ ๊ธ๋ค์ด ์ข
์ข
์์ง๋์ด์ ๊ทธ๋ฐ ๊ฒ ๊ฐ๊ธฐ๋ ํ๋ค.
๊ฐ์
/ํด์ง ํธ๋๋" ๊ฐ์
/ํด์ง ํธ๋๋ ใ๋ค์์ผ๋ก๋ ํด๋ฆญ์. ๋์น๊ฐ ๋น ๋ฅธ ๋ถ๋ค์ ์ด๋ฏธ ์๊ณ ์๊ฒ ์ง๋ง ์ด๋ฉ์ผ์์ ํด๋ฆญ ์ ์๋ฒ์์ ๊ฐ์ข
๋ก๊น
์ ํ๊ณ ๋์ด๊ฐ๊ฒ ๋๋ค. ๊ทธ๋ฌ๋ค ๋ณด๋ ํด๋ฆญ ์ฑํฅ(?)์ ๋ํด ์ง๊ณ๋ ๊ฐ๋ฅํ๋ฐ ์๋ ์งํ๋ฅผ ๋ณด๋ฉด ์ค์ ์ผ๊ณผ๋ฅผ ์์ํ๋ฉด์ ๋ฉ์ผ๋ก ์ข
ํฉ๋ ๊ธฐ์ ๋ธ๋ก๊ทธ ๋ค์ ์ฝ๊ธฐ ์์ํ๊ณ ๊ทธ์ค์์ ํนํ ์์์ผ - 10์๊ฐ ๊ฐ์ฅ ๋ง์ ํด๋ฆญ์๊ฐ ์ง๊ณ๋์๋ค.
ํด๋ฆญ์ ํธ๋๋ | ์๊ฐ+์์ผ ๋ณ ํด๋ฆญ์ ํธ๋๋ | ์๊ฐ+์์ผ ๋ณ ํด๋ฆญ์ ํํธ๋งต" ํด๋ฆญ์ ํธ๋๋ | ์๊ฐ+์์ผ ๋ณ ํด๋ฆญ์ ํธ๋๋ | ์๊ฐ+์์ผ ๋ณ ํด๋ฆญ์ ํํธ๋งต ใ์ด ํฌ์คํ
์ ์์ฑํ๊ณ ์๋ ์ง๊ธ๊น์ง ์ฝ 19,000์ฌ ๊ฐ์ ํฌ์คํ
์ ์์งํ๊ณ ๋ฐํํ์๋๋ฐ ๊ทธ์ค์์ ๊ฐ์ฅ ์ธ๊ธฐ ์์๋ ํฌ์คํ
TOP 30 ์ ๋ค์๊ณผ ๊ฐ๋ค. ์๋ฌด๋๋ ๋จ์ฒด ๋ธ๋ก๊ทธ์ ํฌ์คํ
์ ๋ฉ์ผ ์๋จ์ ์์นํ๊ณ ๋
ธ๋์์ผ๋ก ํ
๋๋ฆฌ๋ฅผ ํ์ํด์์ธ์ง ๋๋ถ๋ถ์ ๊ธ๋ค์ด ๋จ์ฒด ๋ธ๋ก๊ทธ์ ํฌ์คํ
์ธ ๊ฒ์ ์ ์ ์๋ค.
์ด ํ์ฌ, ์ด ์ธ์ ์ฟจํจ์ด ์๋๋ค ๋๋๊ณ ์๋ํ๋ ๊ธ LINE ์ ์
SW ๊ฐ๋ฐ์ ์ฝ๋ฉ ํ
์คํธ, ์ด๋ ๊ฒ ๋ง๋ค์ด์ก์ต๋๋ค ์ฐํ
์ฝ์์ ์ฐพ์ ๋๋ง์ ํจ๊ณผ์ ์ธ ๊ณต๋ถ๋ฒ LINE ์๋ฒ ๊ฐ๋ฐ์๊ฐ ๋๊ธฐ๊น์ง ๋ด๊ฐ ์ค๋นํ ๊ฒ๋ค ์ฐ๋ด์ ๋์ด๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์? ํ๊ต์์ ์๋ ค์ฃผ์ง ์๋ 17๊ฐ์ง ์ค๋ฌด ๊ฐ๋ฐ ๊ธฐ์ ๋ฆฌ๋ทฐ ๊ฐ๋จํ๊ฒ ๋ง๋๋ ์ด์ํ ์๋ ํ ๋ฌธํ์ ํ์ LINE์์ ์ ์ง์์ด ์ฌํ ๊ทผ๋ฌดํ๋ฉด์ ์์ฐ์ฑ์ ์ ์งํ๋ ๋ฐฉ๋ฒ Flutter, ์ ์ ํํ์ง ๋ชปํ๋ ์ฃผ์ ๋ฌ ์๊ฐ์ ํ๋ก๊ทธ๋๋ฐ์ ์ ๋๋ก ํ๊ธฐ ์ฐ์ํํ
ํฌ์ฝ์ค : ์๋ก์ด ์์ ๊ธฐํ์๋ ํ์์๋ค. ๊ฐ๋จํ๊ฒ ๊ตฌ์ถํด ๋ณด๋ JavaScript ๊ฐ๋ฐ ํ๊ฒฝ ์ฐ์ํํ
ํฌ์ฝ์ค : ๋๋ง์ ํญ๋ก ์ฐพ๊ธฐ ์ฝ๋๋ฆฌ๋ทฐ ๋ชจ์ ์๋น์ค๋ฅผ ์๊ฐํฉ๋๋ค.
ํ์ฐฝ ์์ ์ ‘์ ์๋’๊ป์ ์ ํด๋์ผ์ ์ปค๋ฆฌํ๋ผ์ ๋ฐ๋ผ๊ฐ๊ธฐ๋ง ํ๋ฉด ํฐ ๋ฌธ์ ์์ด ์ง์์ ํ์ตํ ์ ์์๋ค. ๊ฑฐ๊ธฐ์ ์ฃผ๊ธฐ์ ์ผ๋ก ์น๋ฅด๋ ์ํ์ ํตํด ‘์ ์’๋ผ๋ ํ๊ฐ ๊ธฐ์ค์ผ๋ก ์ผ๋ง๋ ์ ์ฑ์ฅํ๋๋ฅผ ๊ฒ์ฌํ๊ธฐ๋ ํ๋ค. ์กธ์
ํ ์ด๋ ต๊ฒ ์ด๋ ต๊ฒ ์ทจ์
์ ์ฑ๊ณต์ ํ์ฌ ‘์ ์
๊ฐ๋ฐ์’๋ผ๋ ๋ฐฐ์ง๋ฅผ ๋ฌ๊ณ ํ์ฌ์ ์ฒซ ์ถ๊ทผ. ๊ทธ๋ ๊ฒ n ๋
์ด ์ง๋ ์ง๊ธ๊ณผ ๋ผ๋ผ ์์ (?)์ ๋น๊ตํด ๋ณด๋ฉฐ ๊ณผ์ฐ ‘ํ์ต’์ ๋ํ ์ด์ ๊ทธ๋ํ๊ฐ ์์ง๋ ์ฐ์ํฅ ์ค์ธ๊ฐ? ํ๋ ์ง๋ฌธ์ ์ผ๋จ ๋จ์ ๋ถํฐ ์ฌ๋ผ์ค๋ ๊น์ ํ์จ๊ณผ ํจ๊ฒ ์ด์ํ๊ฒ๋ ์์ด ์บ์บํด์ง๋ค.
์ฐ๋ฆฌ๋ ๋ชจ๋ ๋ผ๋ผ ์์ ์ ๊ฐ์ง๊ณ ์๋ค. ์ถ์ฒ : https://www.dogdrip.net/212294087" ์ฐ๋ฆฌ๋ ๋ชจ๋ ๋ผ๋ผ ์์ ์ ๊ฐ์ง๊ณ ์๋ค. ์ถ์ฒ : https://www.dogdrip.net/212294087 ใ๋ฐฐ์์ผ ํ ๊ฒ ๋๋ฌด ๋ง๋ค. ์๋ ๊ทธ๋ณด๋ค ๋ฐฐ์ด ๊ฒ์ ์ด์ ํ์ฉํด์ผ์ง ์ถ์ผ๋ฉด ๋ ์๋ก์ด ๊ธฐ์ ์ด ๋ฑ์ฅํ๋ค. ๊ทธ๋ ๊ฒ ๋งค๋๋ฆฌ์ฆ์ ๋น ์ง๊ณ . ๊ฑฐ๊ธฐ๋ค ํ์ฌ์ผ์ด ๋ฐ์๋ค๋ ํ๊ณ๋ก ์๊ธฐ๊ณ๋ฐ์ ๋ฉ์ถ๋ค ๋ณด๋ฉด ๋จ๋ค๋ณด๋ค ๋ค์ฒ์ง๋ค๋ ์๊ฐ์ ๊ดํ ์๊ดด๊ฐ์ด ๋ค์ด ์ฐ์ธํด ์ง๊ณค ํ๋ค. (์ฝ๋ก๋ ๋ธ๋ฃจ ๋๋ฌธ๋ง์ ์๋๊ฒ ์ง…) ๊ทธ ๊ฐ์ด๋ฐ ํ์ฌ์๋ ์ ๋ง ์ข์ ์ ๋ฐฐ๋๋ค๋ ๋ง๊ณ ๋ฉํ -๋ฉํฐ ๊ด๊ณ๋ฅผ ์ ํ์ฉํ๋ฉด ์ถฉ๋ถํ, ์, ์ฌ๋ฐ๋ฅธ ๊ธธ๋ก ์ฑ์ฅํ ์ ์์ ๊ฒ์ด๋ผ ์๊ฐํ๋ค. ํ์ง๋ง ๊ทธ๋ ๊ฒ ๋๊ตฐ๊ฐ์๊ฒ ‘์์กด’๋ง ํ๋ค ๊ทธ ๋์์ด ์์ด์ง๋ค๋ ์ง ์ฌ์ง์ด ๊ทธ๋ฐ ๋์์กฐ์ฐจ ์์ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ํด์ผ ํ ๊น? ์ ์ ๊ธฐ์ ์ ๋ฐ์ ํ๊ณ ๋ฐฐ์์ผ ํ ๊ฒ๋ค์ ํ์์ฒ๋ผ ๋์ณํ๋ฅด๊ณ ์๋ ๊ฐ์ด๋ฐ ‘ํ์ฌ์’์์ ๋์๊ฐ ‘๊ฐ๋ฐ์’๋ก์จ ์ฑ์ฅ์ ํ๊ธฐ ์ํด์๋ ์ด๋ ํ ๋ฐฉ๋ฒ์ด ์์๊น?
ใ์ด๋ฒ ํฌ์คํ
์์๋ ๊ฐ๋ฐ์๋ก ์ด์๊ฐ๋ฉด์ ์ฑ์ฅํ๊ธฐ ์ํ์ฆ, ์๊ธฐ๊ณ๋ฐ์ ‘๋ฐฉ๋ฒ’์ ๋ํด ์ด์ผ๊ธฐํด๋ณด๋ ค ํ๋ค. ์ด๊ฒ์ด ์ ๋ต์ด๋ค ํ๋ ์ ํํ์ ์๊ฐํ๋ ค๋ ๊ฒ์ ์๋๋ค. ํนํ ๊ฐ๋ฐ์๋ก์์ ์์ ๋ง๊ฐ(?) ํ ๋๊น์ง๋ ๊ณ์ ๋ฐฐ์์ผ ํ๋ ์๋ช
๊ณผ๋ ๊ฐ์ ์ง์
์ด๊ธฐ์ ์ฒซ ๋จ์ถ๋ฅผ ์ ๋ผ์์ ๊ฐ์์ค๋ฌ์ด ๊ธฐ์ ์ ๋ณํ์ ์ผํฌ์ผ๋น ํ์ง ์๊ณ ์คํ์ง์ฒ๋ผ ๋ฌด์์ด๋ ํก์ํ๋. ๋ง๋๋ง๋ํ ์ ์ ์ ๊ฐ๊ธฐ ์ํจ์ด๋ผ๊ณ ๋ ํ ๊น.
๋ธ๋ก๊ทธ ใ๊ฐ๋ฐ์๊ฐ ๊ธ๋ ์จ์ผ ํ๋?๋ผ๋ ์ง๋ฌธ์๋ ํ์๊ฐ ์์ ์ ์ ๋ฆฌํด๋ ๊ฐ๋ฐํ๊ธฐ ๋ฐ์๋ฐ ๊ธ๊น์ง ์ฐ๋ผ๊ณ ? (๊ธ์ฐ๋ ๊ฐ๋ฐ์๊ฐ ๋์.)๋ผ๋ ๊ธ์ ์ฐธ๊ณ ํด๋ด๋ ์ข์ ๊ฒ ๊ฐ๋ค. ํด๋น ํฌ์คํ
์์ ์์ฐจ๋ก ๊ฐ์กฐํ์์ง๋ง ๊ทธ๋งํผ ๊ฐ๋ฐ์์๊ฒ๋ ํนํ๋ ๊ธ์ฐ๊ธฐ๊ฐ ์ค์ํ๊ณ ํ์ํ๋ค. ๊ธ์ ๊ผญ ‘์’์จ์ผ ํ๋ค๋ ๋ถ๋ด์ ๊ฐ์ง ํ์๋ ์๋ค. (ํ์๋ ๊ทธ๋ ๊ฒ ์ ์ฐ๋ ํธ์ ์๋๋ค…) ๋ค๋ง ๋ฌด์ธ๊ฐ๋ฅผ ๊ธฐ๋กํ๊ณ ์ ๋ฆฌํ๊ณ ์์ ๋ง์ ๊ธฐ์ค์ ๋ง์ถ์ด ์ฌ ์ ๋ฆฌํ๋ ์ต๊ด์ ๊ธฐ๋ฅด๋ค ๋ณด๋ฉด ์ด๋ฌํ ์๊ฐ๋ค์ด ๊ฐ๋ฐ์ ํ ๋์๋ ๋์์ด ์๋นํ ๋์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ฐ๋ฐ์ ํ๋ค๋ณด๋ฉด ๊ผผ๊ผผํ๊ฒ ์ฒดํฌํด์ผํ ์์ธ๊ฐ ๋๋ฌด ๋ง๋ค. ์ถ์ฒ : https://gfycat.com/ko/menacingeducatedatlasmoth" ๊ฐ๋ฐ์ ํ๋ค๋ณด๋ฉด ๊ผผ๊ผผํ๊ฒ ์ฒดํฌํด์ผํ ์์ธ๊ฐ ๋๋ฌด ๋ง๋ค. ์ถ์ฒ : https://gfycat.com/ko/menacingeducatedatlasmoth ใ๋ณต์กํ ๊ตฌ์กฐ๊ฐ ํ์๋ก ํ๋ ๊ฐ๋ฐ์ ํด์ผ ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์. ์ฐ๋ํ๋ ์์คํ
๋ ๋ง๊ณ ์ ๋ง ๋ค์ํ ์๊ตฌ ์ฌํญ์ ํ๋์ ์์คํ
์์ ๊ตฌํ์ ํด์ผ ํ ๊ฒฝ์ฐ ๋ณดํต ๊ฐ๋ฐ์ ํ๊ธฐ์ ์์ ‘์ค๊ณ’๋ผ๋ ๋จ๊ณ๋ฅผ ๊ฑฐ์น๊ธฐ ๋ง๋ จ์ด๋ค. ๊ทธ๋ ๊ธ์ฐ๊ธฐ๋ฅผ ํ์ ๋์ ์ต๊ด(์คํฌ?)์ ์ ์ฉํด ๋ณด๋ฉด ์๊ตฌ ์ฌํญ๋ค ์ค์ ์ค์ํ feature ๊ธฐ์ค์ผ๋ก ์ ๋ฆฌ๋ฅผ ํ๊ฒ ๋๊ณ , ๊ฐ ์ดํด๊ด๊ณ์๋ค์๊ฒ ์ ๋ฆฌํ ๋ถ๋ถ์ ๊ณต์ ํ๋ฉฐ ์์ธ ์ํฉ์ ๋ณด๋ค ๋น ๋ฅด๊ฒ ํ์ธํ ์๋ ์๋ค. ์ฌ์ง์ด ์ฝ๋ ๋ ๋ฒจ์์๋ ์ง๋๋ฐค์ ์ผ์์ผ๋ก ๋จน์ ๋ผ๋ฉด ๋ฉด๋ฐ์ฒ๋ผ ๊ผฌ์ฌ์๋ ๋ถ๋ถ๋ค์ ๋ณด๋ค ๊ฐ๋ฐํ๊ธฐ ํธํ๊ณ ์ ์ง ๋ณด์๊ฐ ์ฉ์ดํ๊ฒ ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ๋ ‘์ ๋ฆฌ’์ ์ต๊ด ๋ํ ๊ธ์ฐ๊ธฐ๋ฅผ ํตํด์ ์๋ จ์ ํ ์ ์๋ค. ์ด๋ฌํ ‘๊ผผ๊ผผํจ’์ ๊ธฐ๋ฅด๋ ๋ฐ์๋ ๊ธ์ฐ๊ธฐ๋ง ํ ๊ฒ ์๋ค๊ณ ์๊ฐํ๋ค.
ใ์ฐ๋ฆฌ๋ ๋ค์ํ ๊ฐ๋ฐ ์ธ์ด๋ก ์ฝ๋ฉ์ ํ๊ณค ํ๋ค. ์ ์ฝ๊ธฐ์ข์ ์ฝ๋๊ฐ ์ข์ ์ฝ๋๋ผ๋ ์ฑ
์ด ์๋ฏ์ด ๊ฒฐ๊ตญ ์ฝ๋ฉ ๋ํ ์ปค๋ฎค๋์ผ์ด์
์ด ์ผ์ข
์ด๋ผ ์๊ฐํ๋ค. ๋ด๊ฐ ์๊ฐํ๋ ๋ก์ง์ ๊ฐ๋ฐ ์ธ์ด๋ก ์ฝ๋ฉ์ ํด์ผ ํ๋ ์ํฉ์ด๋ฉด, ๊ฒฐ๊ตญ ๋ด๊ฐ ์๊ฐํ๋ ๋ก์ง์ด ๋ช
๋ฃํ๊ณ ์ ๋ฆฌ๊ฐ ์ ๋ ์ํ์์์ผ ์ฝ๋ ๋ํ ์์ ‘์ฝ๊ธฐ ์ข์ ์ฝ๋’๊ฐ ๋์ง ์์๊น ์ถ๋ค.
ใ๋ธ๋ก๊ทธ๋ฅผ ์์ํ ๋ ์ด๋์๋ถํฐ ์์ํด์ผ ํ๋ ๋ง๋งํ๋ค๋ฉด, ์ค๋์ ๋ฐฐ์ด ๋ด์ฉ (๊ฐ๋ฐ์๋ค ์ฌ์ด์์ ์ ํ์ฒ๋ผ ๋ฒ์ง๊ณ ์๋ TIL์ ๋ํด์ ์ ๋ฆฌํด ๋ณด๋ ๊ฒ๋ถํฐ ์ถ์ฒํ๋ค. ๊ฒฝ๋ ฅ์ด 1๋
์ฐจ์ฌ๋ 10๋
์ฐจ์ฌ๋ ๊ฐ๋ฐ์ ํ๋ค ๋ณด๋ฉด ์๋ก์ด ๊ฒ์ ๋ฐ๊ฒฌํ๊ธฐ ๋ง๋ จ์ด๋ค. ๊ทธ๋ ๊ฒ ์กฐ๊ธ์ฉ ์ ์ ํ ๋ธ๋ก๊ทธ ํ๋ซํผ์ ์ ๋ฆฌ๋ฅผ ํด ๋๊ฐ๋ค ๋ณด๋ฉด ์ด๋์ ์์ ๋ง์ ๊ฐ๋ฐ ํ์คํ ๋ฆฌ๊ฐ ๋ง๋ค์ด์ง๊ณ , ๋์๊ฐ ๊ธ์ฐ๊ธฐ๊ฐ ์ ํด์ฃผ๋ ๊ธ์ ์ ์ธ ํจ๊ณผ๋ฅผ ๋ง๋ฝํ๋ฆฌ๋ผ ์๋ถํ๋ค.
ํ๋์ ๊ธ์ ์ฐ์ง ์์๋ค. ๊ธ์ ์ฐ์ง ์์ ๊ฒ์ผ๊น ์ฐ์ง ๋ชปํ ๊ฒ์ผ๊น. ์ด๋ฐ์ ๋ฐ ์ด์ ๋ก ๋ฒ์์ ๋ช์ ๋น ์ ธ๋ฒ๋ ค ์๋ฌด๊ฒ๋ ํ๊ธฐ ์ซ์ด์๋ผ๋ ํ๊ณ๊ฐ ์ด์ธ๋ฆด ์๋ ์๊ฒ ๋ค๋ง. ์์ฆ ๋ค์ด ๋์ฑ๋ ๋ฌด๊ธฐ๋ ฅํจ์ด ๊ทน๋๋ก ๋ฟ๋ฟ๋๋ ๊ฐ์ด๋ฐ ๋ฌธ๋, ๊ฐ๋ฐ์๋ก์จ ์ผ๋ง๋ ์ ์ง๋ด์๋๊ฐ ๋ค๋ฅผ ๋์๋ณด๊ณ ์ถ์๋ค. ์๋ง ๋ณด๊ณ ๋ฌ๋ฆฌ๋ ๊ฒ๋ณด๋ค ๋ด ์๊ฐ๊ณผ ๋ด ํธํก์ ์ ๊ฒํ๋ ๊ฒ ๋ํ ์ค์ํ๋ค๊ณ ์๊ฐํ๊ธฐ์ ๋น๋ถ๊ฐ์ ๋ ๋์ ๊ฐ๋ฐ์๊ฐ ๋๊ธฐ ์ํ ์ฌ๋ฌ ๊ฐ์ง ์ฃผ์ ๋ก ๊ธ์ ์จ๋ณด๋ ค ํ๋ค. ์ด๋ฆํ์ฌ ๊ทธ๋ฐ ๊ฐ๋ฐ์๋ก ๊ด์ฐฎ์๊ฐ XX ํธ
์ด๋๊น์ง๋ ํ์์ ์๊ฐ์ ๋ํด ์ ๋ ๊ฒ์ผ ๋ฟ ๋ด์ฉ์ด ์๋ชป๋์์ ์๋ ์๋ค. ์ฆ, ์ ๋ต์ด ์๋๋ผ๋ ์ด์ผ๊ธฐ. ํ์์ ์ด๋ฌํ ํฌ์คํ
์ผ๋ก ์ด ๊ธ์ ์ฝ๋ ์ฌ๋ฌ๋ถ๋ค๋ ์์ ๋ง์ ๊ฐ์น๊ด์ ์ ๋ฆฝํด๋ณด๋ ๊ธฐํ๊ฐ ๋๊ณ ๋์๊ฐ ๋ชจ๋๊ฐ ๋ ๋์ ๊ฐ๋ฐ์๋ก ํ๊ฑธ์ ์ฌ๋ผ์๋ ์๋ฆ๋ค์ด ์ธ์์ ๊ฟ๊พธ๋ ๋ง์์ผ๋ก ์์ ๋ ๊ฐฏ์ง์ ํด๋ณธ๋ค.
ใ๊ฐ๋ฐ์๋ก ์ด์๊ฐ๋ ๋ฐ ์์ด ๊ฐ์ฅ ์ค์ํ ๊ฒ ๋ฌด์์ผ๊น? ๋ฌผ๋ก ๊ฐ๋ฐํ ์ ์๋ ๊ธฐ์ ์ด ๊ฐ์ฅ ์ค์ํ๊ฒ ์ง๋ง ๋ช ๋
์ ๋ถํฐ ๊ธฐ์ ์ ๋ฐ์ ์ด ๊ธ๋ณํ๋ ์ธ์ ์์์ ๊ณผ์ฐ ๊ธฐ์ ๋ง์ด ์ค์ํ ๊น? ๊ธฐ์ ๋ง ์ ์๊ณ ์์ผ๋ฉด ๋ณต์กํ๊ฒ ๊ผฌ์ธ ์คํ๊ฒํฐ ๋ฉด ๊ฐ์ ๋ฌธ์ ๋ง์ ์ฝ๋๋ฅผ ์ ์ ํ์ดํค์น๊ณ , ์ธ์ ์ด๋์๋ ๊ฐ๋ฐ์๋ก์จ ํ๋ณตํ ์ถ์ ์์ ํ ์ ์์๊น?
ใ์ฌ๋ฌ ๊ฐ์ง ์ค์ํ ์์๋ค ์ค ๊ฐ์ฅ ์ฒซ ๋ฒ์งธ๋ก ๋ ์ค๋ฅด๋ ํค์๋๋ ๋ฐ๋ก ๋ฌธํ(Culture)๊ฐ ์๋๊น ์ถ๋ค. ๊ทธ๋ผ ์ ๋ฌธํ๊ฐ ๊ฐ๋ฐ์์๊ฒ ์ค์ํ๊ณ ์ด๋ค ์์ผ๋ก ๋ฌธํ๋ฅผ ๋ง๋ค์ด ๊ฐ๋ ๊ฒ ์ข์์ง์ ๋ํด ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค.
๊ฐ ํ์ ๋ง๋ ๋ฌธํ๋ ๋ชจ๋๋ฅผ ์ฑ์ฅ์ํฌ ์ ์๋ค. ์ถ์ฒ : https://steemkr.com/kr-dev/@dreamisnowhere/5squ7b" ๊ฐ ํ์ ๋ง๋ ๋ฌธํ๋ ๋ชจ๋๋ฅผ ์ฑ์ฅ์ํฌ ์ ์๋ค. ์ถ์ฒ : https://steemkr.com/kr-dev/@dreamisnowhere/5squ7b ใ๊ฐ๋ฐ์๋ผ๋ ์ง์
์ ๊ฐ์ง๊ณ ์๋ ๋ถ๋ค ์ค์ ํ๋ฆฌ๋์๋ 1์ธ ์คํํธ์
์ ์ด์ํ๋ ๋ถ๋ค์ ์ ์ธํ๊ณ . ๋๋ถ๋ถ์ ์ฌ๋๋ค์ ์ฌ๋ฌ ๋ช
๊ณผ ํจ๊ป ๊ณต๋์ ๋ชฉํ๋ฅผ ๋ฌ์ฑํ๊ธฐ ์ํ “ํ"์ด๋ผ๋ ๋จ์์ ์์๋์ด ๊ฐ๋ฐ์ ํ๊ณ ์๋ค. ์ผ๊ทผ์ ๋งค์ผ ๋ฐฅ ๋จน๋ฏ์ด ํ๋ ์กฐ์ง๋ ์์ ํ
๊ณ ์ด๋ฅธ๋ฐ ์๋ผ๋ฒจ์ ์ ์งํค๋ฉฐ ๋ฃ๊ธฐ๋ง ํด๋ ๋ฐ๊ฐ์ด ์๋ฆฌ์ธ “์นผํด"๋ฅผ ๋ฐฅ ๋จน๋ฏ์ด ํ๋ ์กฐ์ง๋ ์์ ํ
๊ณ . ์ฌ๊ธฐ์ ๋งํ๊ณ ์ ํจ์ ์ด๋ฌํ ์ผ๊ทผ vs ์นผํด์ฒ๋ผ “๊ทผ๋ฌด ์๊ฐ์ ์"์ ๋ํด ์ด์ผ๊ธฐํ๋ ค๋ ๊ฑด ์๋๋ค. ํ์ฌ, ๋ ๊น๊ฒ๋ ํ ๋ด์์ ์ด๋ค ๋ฌธํ ์์์ ๊ฐ๋ฐ์๋ก ์ด์๊ฐ๊ณ ์๋์ง์ ๋ํด ์ด์ผ๊ธฐํ๋ ค ํ๋ค.
์ฝ๋๋ฆฌ๋ทฐ ใํ์ ์ํด์ ๊ฐ๋ฐ์ ํ๋ค ๋ณด๋ฉด ๊ฐ์ ์ฝ๋๋ฅผ ๋์์ ์์
ํ๊ณค ํ๋ค. ๊ทธ๋์ ํ์๊ด๋ฆฌ ๋๊ตฌ (์์ฆ git ์ ์ ์ฐ๋ ๊ณณ์ด ์์ ์ ๋…)๋ฅผ ์ฌ์ฉํด์ ๋์์ ๊ฐ๋ฐ์ ์งํํด๋ ์ ํ ๋ฌด๋ฆฌ๊ฐ ์์ ์ ๋์ธ๋ฐ ๊ฒฐ๊ตญ ์์
ํ ๊ฒฐ๊ณผ๋ฌผ์ ํ ๊ณณ์ผ๋ก ๋ณํฉ (merge) ํด์ผ ํ๋ ์์ ์ด ์ค๊ธฐ ๋ง๋ จ์ด๊ณ ๊ทธ๋์ (์จ๋ผ์ธ/์คํ๋ผ์ธ) ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํ๊ฒ ๋๋ค. ์ด๋ ํ ์ฌ์ฐ์ผ๋ก ์ฝ๋ ๋ฆฌ๋ทฐ ์์ด ๋นจ๋ฆฌ merge ํด์ผ ํ๋ ๊ฑด ์ดํด๋์ง๋ง ๊ฐ๊ธ์ ํ ๋ช
์ด์์ ๋ฆฌ๋ทฐ์ด๊ฐ ์น์ธ์ ํ ๋ค์ merge ๊ฐ ๋ผ์ผ ํ๋ค๊ณ ์๊ฐํ๋ค. (pullRequest๋ฅผ ๋จ์ merge ์ฉ์ผ๋ก ์ฌ์ฉํ๋ ๊ฑด ์ ๋ง ์๋ชป๋ ๋ฐฉ๋ฒ ์ค ํ๋) ์ค๋ณต๋ ์ฝ๋๋ฅผ ๋ง๋ค์๊ฑฐ๋ ์์
์๊ฐ ์์ํ์ง ๋ชปํ ๋ถ๋ถ๋ค์ ๋ฆด๋ฆฌ์ค ์ ์ ์๋ก ์ด์ผ๊ธฐํด๋ณด๋ฉด์ ๋ฒ๊ทธ๋ฅผ ์์ ํ๊ฑฐ๋ ํ ์ปจ๋ฒค์
, ์ค๊ณ/๊ตฌ์กฐ๋ฅผ ๋ ํจ์จ์ ์ผ๋ก ๊ฐ์ ธ๊ฐ ์ ์๋ ์ ํธ์ ์ฐฌ์ค.
ใ์ฌ๊ธฐ์ ์ค์ํ ํฌ์ธํธ๋ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ๋ ‘๋ฆฌ๋ทฐ์ด’ ์ ๋ฆฌ๋ทฐ๋ฅผ ํด์ฃผ๋ ‘๋ฆฌ๋ทฐ์ด’๋ค์ ๋ฌธํ์ ์ธ ์ธก๋ฉด์์ ์๊ฐ์ ํด๋ณผ ํ์๊ฐ ์๋ค.
๋ฆฌ๋ทฐ์ด(Reviewee) ๋ฆฌ๋ทฐ์ด์ ์์คํ ์๊ฐ์ ํ ์ ํด์ ์์ ์ ์ฝ๋๊ฐ ์ด์์ด ์๋์ง์ ๋ํ ‘๋์’์ ์์ฒญํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์ต๋ํ ์ค๋ช
์ ์ ์ ์ด์ ๋ฆฌ๋ทฐํ๋ ๋ฐ ๋์์ ์ค ์ ์์ด์ผ ํ๋ค. ์์
์ ํ๋ค ๋ณด๋ฉด ํ ๋ฒ์ ๋ชฐ์์ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ๋ ๊ฒฝ์ฐ๊ฐ ๋๋ถ๋ถ์ด์ง๋ง ๊ฐ๋ฐ ์์ฐ์ฑ ์ธก๋ฉด๊ณผ ์ฝ๋ ๋ฆฌ๋ทฐ ์๊ฐ์ ์ค์ด๋ ์ธก๋ฉด์์๋ ์ต๋ํ ์์ ๋จ์๋ก ๋ฆฌ๋ทฐ๋ฅผ ์์ฒญํด์ผ ํ๋ค. ๋ฆฌ๋ทฐ๊ฐ ์งํ์ด ๋์ง ๋ชปํ์ฌ ๋ค์ ์์
๋ํ ์งํ์ ๋ชปํ๋ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด ์ต๋ํ ์ฝ๋ ๋ฆฌ๋ทฐ ๋ฐ๋ ๋ถ๋ถ๊ณผ ์์กด์ฑ์ด ์๋๋ก ์์
์ด ๋ผ์ผ ํ๋ฉฐ ๊ทธ๋ ์๋๋ผ๋ฉด ์ ์คํ๊ฒ ๋ฆฌ๋ทฐ์ด์๊ฒ ‘๋ถํ’์ ํด์ผ ํ๋ค.
์น ์ดํ๋ฆฌ์ผ์ด์
์์ ํํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ์ค๋ณต์ผ๋ก ์ฒ๋ฆฌ๋๋ ๋ด์ฉ์ ํ๊ณณ์์ ์ฒ๋ฆฌํ ์ ์๋ค๊ฑฐ๋ ์๋น์ค์ ๋ค์ํ ๋์ฆ๋ฅผ ์ถฉ์กฑ์ํค๊ธฐ์ ์์ฑ๋ง์ถค์ธ ์ฅ์น์ธ๊ฒ ๊ฐ๋ค. ํํฐ๋ ๋ฌด์์ธ๊ฐ ์ ๋ํ ๋ด์ฉ์ ์๋์ ๋ค๋ฅธ ๋ธ๋ก๊ทธ๋ ๊ณต์ ๋ํ๋จผํธ์์ ์์ธํ๊ฒ ๊ทธ๋ฆฌ๊ณ ๋ค์ํ๊ฒ ์ค๋ช
ํ๊ณ ์๊ธฐ์ ๊ธฐ๋ณธ ๊ฐ๋
์ ๋ํด์๋ ์ค๋ช
ํ์ง ์๋๋ก ํ๋ ค ํ๋ค. ์ด๋ฒ ํฌ์คํ
์์๋ ์คํ๋ง ๋ถํธ๋ฅผ ์ฌ์ฉํ๋ฉด์ ์ด๋
ธํ
์ด์
์ด๋ผ๋ ๊ฐํธํจ์ ์ทจํด(?) “๋๊ฒฉ ์์ผ๋ก, ๋ฅ๊ณต” ์ ์์ธ๋ก ๊ฐ๋ฐ์ ํ๋ คํ๋ ํ์๋ฅผ ๋ณด๊ณ “๋ฐ์ฑ"์ ์์ธ๋ก ํํฐ๋ฅผ ๋ฑ๋กํ๋ ๋ฐฉ๋ฒ์ ๋ํด ๋ช
ํํ๊ฒ ์ ๋ฆฌ๋ฅผ ํ๊ณ ์ ํ๋ค. ๋ง์ง๋ง์ผ๋ก๋ ์์ฃผ ๊ฐ๋จํ๋ฉด์๋ ์์ฒญ๋๊ฒ ์ํํ ํํฐ ์ค์ ์ฌ๋ก์ ๋ํด์๋ ์ง๊ณ ๋์ด๊ฐ๋ณด์. ๊ทธ๋ฅ ๋์ด๊ฐ๋ฉด ์์ฌ์ฐ๋, ํ๋ฒ์ด๋ผ๋ ‘spring’ ์ด๋ผ๋ framework ๋ฅผ ์ ํด๋ณธ ์ฌ๋์ด๋ผ๋ฉด ๋ดค์๋ฒํ ๊ทธ๋ฆผ์ ์ฒจ๋ถํ๋๊ฒ์ผ๋ก ํํฐ๋ ๋ฌด์์ธ๊ฐ ์ ๋ํ ์ค๋ช
์ ๋์ ํ๋๊ฒ ์ข๊ฒ ๋ค.
์ถ์ฒ : https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/" ์ถ์ฒ : https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/ ๋ฐฉ๋ฒ์ ์ค๋ช
ํ๊ธฐ ์ ์ ๋์ผํ๊ฒ ์ฌ์ฉ๋ ํํฐ์ ์ปจํธ๋กค๋ฌ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
ํํฐ @Slf4j public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("init MyFilter"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { log.info("doFilter MyFilter, uri : {}", ((HttpServletRequest)servletRequest).getRequestURI()); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { log.info("destroy MyFilter"); } } ํ
์คํธ ํ ์ปจํธ๋กค๋ฌ @Slf4j @RestController public class SampleController { @GetMapping("/test") public String test() { return "test"; } @GetMapping("/filtered/test") public String filteredTest() { return "filtered"; } } ๋ฐฉ๋ฒ 1 : FilterRegistrationBean ์์ฃผ ๊ฐ๋จํ๊ฒ, ์ผ๋ฐ url ํ๋์ ํํฐ์ ์ ์ฉํ url ๋๊ฐ๋ฅผ ๋ง๋ค๊ณ ์ค์ ํ๋ ค ํ๋ค. FilterRegistrationBean ์ ์ด์ฉํด์ ์์์ ๋ง๋ค์๋ ํํฐ๋ฅผ ์๋์ฒ๋ผ ๋ฑ๋กํด๋ณด์.
@SpringBootApplication public class Method1Application { public static void main(String[] args) { SpringApplication.run(Method1Application.class, args); } @Bean public FilterRegistrationBean setFilterRegistration() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter()); // filterRegistrationBean.setUrlPatterns(Collections.singletonList("/filtered/*")); // list ๋ฅผ ๋ฐ๋ ๋ฉ์๋ filterRegistrationBean.addUrlPatterns("/filtered/*"); // string ์ฌ๋ฌ๊ฐ๋ฅผ ๊ฐ๋ณ์ธ์๋ก ๋ฐ๋ ๋ฉ์๋ return filterRegistrationBean; } } ์ ์ฃผ์์๋ ์ ์์ง๋ง filterRegistrationBean ์ “setUrlPatterns” ์ “addUrlPatterns” ์ ์ฐจ์ด๋ ๋ณ๊ฑฐ ์๋ค. list ์์ฒด๋ฅผ ๋ฐ์๊ฑด์ง ์๋๋ฉด ๊ฐ๋ณ์ธ์๋ก ๊ณ์ ์ถ๊ฐ ํ ๊ฒ์ธ์ง. ์ด๋ ๊ฒ ๋๋ฉด “/filtered/“์ผ๋ก “์์"ํ๋ ํจํด์ url์ ์์ฒญ์ด ์ค๊ฒ ๋๋ฉด ๋ฑ๋กํ ํํฐ๋ฅผ ํต๊ณผํ๊ฒ ๋๋ค.
์คํ : ํํฐ ์์ฑ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.6.RELEASE) 2020-04-06 23:45:01.225 INFO 14672 --- [ main] c.t.s.method1.Method1Application : No active profile set, falling back to default profiles: default 2020-04-06 23:45:02.153 INFO 14672 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2020-04-06 23:45:02.168 INFO 14672 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2020-04-06 23:45:02.168 INFO 14672 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.33] 2020-04-06 23:45:02.361 INFO 14672 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2020-04-06 23:45:02.362 DEBUG 14672 --- [ main] o.s.web.context.ContextLoader : Published root WebApplicationContext as ServletContext attribute with name [org.springframework.web.context.WebApplicationContext.ROOT] 2020-04-06 23:45:02.362 INFO 14672 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1082 ms 2020-04-06 23:45:02.391 DEBUG 14672 --- [ main] o.s.b.w.s.ServletContextInitializerBeans : Mapping filters: filterRegistrationBean urls=[/filtered/*] order=2147483647, characterEncodingFilter urls=[/*] order=-2147483648, formContentFilter urls=[/*] order=-9900, requestContextFilter urls=[/*] order=-105 2020-04-06 23:45:02.391 DEBUG 14672 --- [ main] o.
์ง๋ ํฌ์คํ
์์๋ Retryable ๋ฅผ ํ์ฉํด์ ๊ฐํ์ ์ธ ๋คํธ์ํฌ ์ค๋ฅ๋ฅผ “์ฌ์๋"๋ฅผ ํจ์ผ๋ก์จ ์์ฃผ ๊ฐ๋จํ๋ฉด์๋ ๊ฐ๋ ฅํ๊ฒ ํด๊ฒฐํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด์๋ค. ์ค์ ๋ก ํ์๊ฐ ์ด์ํ๋ ์๋น์ค ์์๋ Retryable ๋ฅผ ์ด์ฉํ๊ธฐ ์ ๊ณผ ํ๋ฅผ ๋น๊ตํด๋ณด๋ฉด ๊ฐํ์ ์ธ ๋คํธ์ํฌ ์ค๋ฅ์ ๋น๋์๊ฐ ํ์คํ ์ค์ด๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค. ์ด๋ ๊ฒ “์ฌ์๋"๋ฅผ ํด์ ์์ฒญํ์๋ ์ฑ๊ณต ์๋ต์ ๋ฐ์ ๊ฒฝ์ฐ์ ๋ฌธ์ ๊ฐ ์๋์ง๋ง ๋คํธ์ํฌ ์ค๋ฅ๊ฐ ์๋ ์ค์ ๋ก ํธ์ถ์ ๋ฐ๋ ํด๋น ์๋ฒ์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค๋ฉด ์ด๋จ๊น? ์์ปจ๋, ํด๋น ์๋ฒ์์ DB๋ฅผ ์กฐํํ๋ API๋ฅผ ํธ์ถํ๋ค๊ณ ๊ฐ์ ํ์๋ DB ์์ฒด์์ ์ด๋ ํ ์ค๋ฅ๊ฐ ๋๋ค๋ฉด. ์ด๋ฐ ๊ฒฝ์ฐ๋ ๋จ์ํ “์ฌ์๋"๋ก ํด๊ฒฐํ ์ ์๋ ๋ฌธ์ ๋ค.
๋ฌผ๋ก Retryable ์ Recover ์ด๋
ธํ
์ด์
์ ํ์ฉํ๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ ์ฆ, ์ฌ์ฉ์์๊ฒ๋ ์ค๋ฅ์๋ต์ด ๋ฐ์์ ์ํ๊ฒ ์ง๋ง ํธ์ถ ๋ฐ๋ ์๋ฒ ์์ฒด์์์ ์๋ฌ๊ฐ ๋ฐ์ํ๋๋ฐ ์ด๋ฐ์์ ์ฌ์๋๋ฅผ ๊ณ์ ์๋ํ๋ค๋ฉด ํธ์ถ ๋ฐ๋ ์๋ฒ ์
์ฅ์์๋ ์ด “์ฌ์๋” request ๋ํ “๋ถํ” ๋ก ๋ฐ๊ฒ ๋๊ณ ๊ฒฐ๊ตญ 2์ฐจ, 3์ฐจ ์ฅ์ ๊ฐ ์ด์ด์ง ์ ๋ฐ์ ์๋ค.
๊ธฐ์กด ํ๋ฉ์ด๋ฆฌ๋ก ๊ด๋ฆฌ๋๋ Monolithic Architecture ์์๋ ์์ฒด์ ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์ ์ด๋ฌํ ์๋ฌ ์ปจํธ๋กค ๋ํ ์์ฒด์ ์ผ๋ก ๊ด๋ฆฌ๋ฅผ ํ ์ ์์ง๋ง, ๋ชจ๋์ด ๋ชจ๋์ ํธ์ถํ๊ฒ ๋๋ Microservice Architecture ๋ก ๋ฐ๋๋ค๋ณด๋ ์ด๋ฐ “์ฐ์ ์ฅ์ (?)” ๊ฐ์ ํ์์ด ๋ฐ์ํ๊ฒ ๋๋ ๊ฒฝ์ฐ๊ฐ ์๋ค. ํธ์ถ์ ๋ฐ๋ ์๋ฒ์ ์ํ๊ฐ ์ด์ํ๋ฉด (์๋ฌ์๋ต์ด ์ง์ ํ ์๊ณ์น๋ฅผ ๋ฒ์ด๋๋ ์์ค์ผ๋ก ๋ง์ถฐ์ ๋ฐ์ํ๋ค๋ฉด) ์ ์ ํ๊ฒ ํธ์ถ์ ํ์ง ์๊ณ (2์ฐจ ์ฅ์ ๋ฅผ ๋ด์ง ์๋๋ก ํธ์ถ ์์ฒด๋ฅผ ํ์ง ์๊ณ ) ์ด๋์ ๋ ๊ธฐ๋ค๋ฆฌ๋ค ํด๋ผ์ด์ธํธ์๊ฒ๋ ์๋ฌ์๋ต์ด ์๋ ๋ฏธ๋ฆฌ ์ ํด๋ ์๋ต์ ๋ด๋ ค์ฃผ๊ณ , ์๋ฌ๊ฐ ๋ณต๊ตฌ๋๋ฉด ๋ค์ ํธ์ถํ๋๋ก ํ๋ “๋ฌด์ธ๊ฐ” ๊ฐ ํ์ํ์ง ์์๊น?
์ฐ์ ์ฅ์ . ์ ๋ฐ ๋ฉ์ถฐ… ์ถ์ฒ : http://dpg.danawa.com/mobile/community/view?boardSeq=175&listSeq=4066389" ์ฐ์ ์ฅ์ . ์ ๋ฐ ๋ฉ์ถฐ… ์ถ์ฒ : http://dpg.danawa.com/mobile/community/view?boardSeq=175&listSeq=4066389 ์ง๋ ํฌ์คํ
์ ์ด์ด ์ด๋ฒ ํฌ์คํ
์์๋ ๊ทธ “๋ฌด์ธ๊ฐ”. ์ฆ, Circuit-breaker ์ ๋ํด ์์๋ณด๊ณ ์ง์ ๊ตฌํ ๋ฐ ํ
์คํธ ํ๋ฉด์ ๋์๊ฐ๋ ์๋ฆฌ์ ๋ํด ์ดํด ํด๋ณด๊ณ ์ ํ๋ค. ๋ง์ ๊ฐ๋
์ ๋จธ๋ฆฟ์์ ์์ง๋ง ์ง์ ๊ตฌํํด๋ณด์ง ์์ผ๋ฉด ๋ด๊ฒ์ด ์๋๊ธฐ์, ์ง์ ๊ตฌํํ๊ณ ์ค์ ๊ฐ๋ค์ ๋ฐ๊ฟ๊ฐ๋ฉด์ ์ธ์ ๊ฐ ํ์ํ ์๊ฐ์ ๊บผ๋ด์ ์ฌ์ฉํ ์ ์๋ ๋๋ง์ “๋ฌด๊ธฐ” ๋ฅผ ๋ง๋ค์ด ๋ณด๊ณ ์ ํ๋ค.
Circuit breaker ? (ํ๊ตญ ๋ฐ์์ผ๋ก) ์ํท๋ธ๋ ์ด์ปค๋ฅผ ๊ฒ์ํด๋ณด๋ฉด ์ฃผ์์์ฅ ๊ด๋ จ๋ ๋ด์ฉ์ด ๊ฝค ๋์จ๋ค. (์, ์ ๊น ๋๋ฌผ์ข…) ์ํท ๋ธ๋ ์ด์ปค. ์ด ์ฉ์ด๋ ๋ค์ํ ๊ณณ์์ ์ฌ์ฉ๋๋๋ฐ “ํ๋ก ์ฐจ๋จ๊ธฐ” ๋ผ๊ณ ๋ ๊ฒ์์ด ๋๋ค. ํด๋น ๋ด์ฉ์ ๋ฐ์ทํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
ํ๋ก ์ฐจ๋จ๊ธฐ๋ ์ ๊ธฐ ํ๋ก์์ ๊ณผ๋ถํ๊ฐ ๊ฑธ๋ฆฌ๊ฑฐ๋ ๋จ๋ฝ์ผ๋ก ์ธํ ํผํด๋ฅผ ๋ง๊ธฐ ์ํด ์๋์ผ๋ก ํ๋ก๋ฅผ ์ ์ง์ํค๋ ์ฅ์น์ด๋ค. ๊ณผ๋ถํ ์ฐจ๋จ๊ธฐ์ ๋์ ์ฐจ๋จ๊ธฐ๋ก ๋๋๋ค. ํจ์ฆ์ ๋ค๋ฅธ ์ ์, ์ฐจ๋จ๊ธฐ๋ ์ด๋ ์ ๋ ์๊ฐ์ด ์ง๋ ๋ค, ์๋์ ๊ธฐ๋ฅ์ด ๋์ํ๋๋ก ๋ณต๊ท๋๋ค.
์ฌ๊ธฐ์ ๊ฐ์ฅ ์ค์ํ ๋ฌธ์ฅ์ “ํผํด๋ฅผ ๋ง๊ธฐ ์ํด ์๋์ผ๋ก ํ๋ก๋ฅผ ์ ์ง์ํค๋”, “์ด๋์ ๋ ์๊ฐ์ด ์ง๋๋ค ์๋์ ๊ธฐ๋ฅ์ด ๋์ํ๋๋ก ๋ณต๊ท๋๋ค” ์ด ๋ถ๋ถ์ด ๊ฐ์ฅ ์ค์ํ ๊ฒ ๊ฐ๋ค. ์์คํ
๊ตฌ์ฑ์ด ์ ์ Microservice Architecture ๋ก ๋ฐ๋์ด ๊ฐ๋ ์์ ์์ ์ด๋ฌํ “์ํท๋ธ๋ ์ด์ปค"๋ ์๋์ผ๋ก ๋ชจ๋๊ฐ์ ํธ์ถ ์๋ฌ๋ฅผ ๊ฐ์งํ๊ณ ์์์ ๋งํ “์ฐ์ ์ฅ์ "๋ฅผ ์ฌ์ ์ ๋ง์ ์ ์๋ ์์ฃผ ์ค์ํ ๊ธฐ๋ฅ์ด๋ผ ์๊ฐ๋๋ค.
“circuit breaker spring” ์ด๋ผ๋ ํค์๋๋ก ๊ฒ์ํด๋ณด๋ฉด ์ด๋ฌํ ๊ณ ๋ฏผ์ ์ด๋ฏธ Netflix ๋ผ๋ ํ์ฌ์์ Hystrix ๋ผ๋ ์ด๋ฆ์ผ๋ก ๊ฐ๋ฐ์ด ๋๊ฒ์ ์ ์ ์๋ค. ์ด core ๋ชจ๋์ Spring ์์ ํ๋ฒ ๋ ๊ฐ์ธ์ Spring Boot ์์ ์ฌ์ฉํ๊ธฐ ์ข๊ฒ spring-cloud-starter-netflix-hystrix ๋ผ๋ ์ด๋ฆ์ผ๋ก ๋ง๋ค์ด ๋ ๊ฒ์ด ์๋๋ฐ ์ด๊ฒ์ ํ์ฉํด ๋ณด๊ธฐ๋ก ํ์.
๊ตฌํ ๋ ๊ทธ๋ฌ๋ฏ์ด SpringBoot ํ๋ก์ ํธ๋ฅผ ๋ง๋ค๊ณ ํ
์คํธํ Controller ๋ฅผ ๋ง๋ค์ด ์ฃผ์. ์๋๋๋ก๋ผ๋ฉด ํธ์ถ์ ํ๋ ๋ชจ๋๊ณผ ํธ์ถ์ ๋ฐ๋ ๋ชจ๋, 2๊ฐ์ ๋ชจ๋์ ๋ง๋ค์ด์ ํ
์คํธ ํด์ผ ํ์ง๋ง ํธ์๋ฅผ ์ํด ํ๋์ ๋ชจ๋์์ ๋๊ฐ์ Controller ์ ๋ง๋ค๊ณ ํ
์คํธ ํด๋ณด๋ ๊ฒ์ผ๋ก ํ์.
@RestController public class MainController { private final MainService mainService; @GetMapping("index") public String index(String key){ return mainService.getResult(key); } public MainController(MainService mainService) { this.mainService = mainService; } } @Slf4j @Service public class MainService { private RestTemplate restTemplate; public String getResult(String key) { return restTemplate.
๊ฐ๋ฐ์๋ก์์ ์ปค๋ฆฌ์ด๋ ์ ๋ง ๋ค์ํ์ง๋ง ํ์๊ฐ ๋ณด๊ณ ๋ค์ ๊ฒฝํ์ ์์ฃผ ์ผ๋ฐํ ์์ผ ์ ๋ฆฌํด ๋ณด์๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
์ฒ์์ ์ ๊ณต/๋น์ ๊ณต์ ๋ถ๋ฌธํ๊ณ ์ ์
์ผ๋ก ๊ฐ๋ฐ์ ์์ํ์ฌ ๋ค์ํ ๊ฐ๋ฐ ๊ฒฝํ์ ํ๊ฒ ๋๋ค. ์ฌ์์๊ฒ ํผ๋๊ธฐ๋ ํด๋ณด๊ณ ๋๋ ํผ๋ด์ค ์ฌ์๊ฐ ์์ด ํผ์ ๋๋ ๋ฐค๋ ์๋ณด๊ณ , ๋คํฌ์ํด๊ณผ ๊ฑฐ๋ถ๋ชฉ์ ๊ฒธ๋นํ ์ด๋ฅธ๋ฐ “์ฝ์ง"์ ํ๋ฉฐ ๊ณ ํต์ ์์ ์ ๋ณด๋ด๊ณ ๋๋ฉด ์ด๋๋ง ์น์ง(์ง๊ธ)์ ํ๋ฉฐ ์ผ์ ๊ท๋ชจ์ “ํ์ฅ(ํน์ ๊ด๋ฆฌ์)“์ด ๋๋ค. ๊ทธ๊ฒ ์์๋ ํ์๋ . ๊ฐ๋ฐ์๋ ๋ค์ “๊ธฐ์ "์ด๋ผ๋ ํน์์ฑ์ ๊ฐ์ง๊ณ ์์ง๋ง ์ด๋ ์ง๊ตฐ์ด๋ ๊ฐ์ ์ด๋ฌํ ์ปค๋ฆฌ์ด ํจ์ค์ ํ๋ฆ์ ๋งค์ฐ ๋น์ทํ๊ฒ ํ๋ฌ๊ฐ๋ ๊ฒ ๊ฐ๋ค. ์ ์ด๋ ํ์๊ฐ ๋ณด๊ณ ๋ค์ ๊ฒ๋ง ๋ณด๋ฉด ๋ง์ด๋ค. (์์ธ ์ผ์ด์ค๋ ํญ์ ์์ง๋ง…)
ํ๋ฃจ๋ ํ์ฅ๋๊ณผ์ ๋ฉด๋ด ์ค์ “์ด์ ๋ ๋ง๋ฅ ๋์์ ์๋ ๊ฐ๋ฐ๋ง ํ ๊ฒ์ด ์๋๋ค. ๊ธฐ์ ์ ์ข ๋ ๊น๊ฒ ๋ค์ฌ๋ค๋ณด๋ ์๋ฆฌ์ ์ฌ๋์ ๊ด๋ฆฌํ๋ฉฐ ์ฃผ์ด์ง ๊ณผ์ ๋ฅผ ์งํํ๋ ์๋ฆฌ, ๋ ์ค ์ ํํด์ผ ํ๋ ์๊ธฐ๊ฐ ์จ ๊ฒ ๊ฐ๋ค. ๋ ๋๊ณ ๋ ๋ฉ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ๋ ๋๊ฒ ๋ณผ ์ค ์์์ผ ํ๋ค.“๋ผ๋ ๋ง์์ ๋ฃ๊ฒ ๋๋ค. ์ด๋๋ง “๊ทธ ์์ "์ด ๋ค๊ฐ์จ ๊ฒ์ด๋ค. ๊ฐ์ธ์ ์ผ๋ก ํ์๋ ํ์ฅ๋์ด ๋ง์ํ์ ๋ ๊ฐ์ง ์ค ์ ์์ ์ข ๋ ๊ฐ๊น๊ฒ ๋ค๊ฐ๊ฐ๊ณ ์ถ๋ค. ๊ทธ๋งํผ ์ค๋์ค๋ “์ค๋ฌด ๊ฐ๋ฐ"์ ํ๊ณ ์ถ๊ณ , ๋ ๊ทธ๋งํผ ๊ฐ๋ฐ์ด ์ฌ๋ฐ๊ธฐ ๋๋ฌธ์ด๋ค. ์์ง๋ ๋์์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๊ฐ๋ฐํ๋ฉฐ ์๊ฐ ๊ฐ๋ ์ค ๋ชจ๋ฅผ ๋งํผ ๋ฐค์ ์์ฐ๋ ๊ฒ ์ฌ๋ฏธ์๋ ๊ฑธ ๋ณด๋ฉด…
์๋ฆฌํ๋ ๊ฑธ ์ข์ํ์ง๋ง ์ด์ํ๊ฒ ์นํจ์ง์ ํ๊ณ ์ถ์ง ์๋ค. ์ถ์ฒ : https://catapult.tistory.com/entry/%EC%B9%98%ED%82%A8%EC%A7%91%EC%9D%B4%EB%82%98-%EC%B0%A8%EB%A0%A4%EC%95%BC%EC%A7%80" ์๋ฆฌํ๋ ๊ฑธ ์ข์ํ์ง๋ง ์ด์ํ๊ฒ ์นํจ์ง์ ํ๊ณ ์ถ์ง ์๋ค. ์ถ์ฒ : https://catapult.tistory.com/entry/%EC%B9%98%ED%82%A8%EC%A7%91%EC%9D%B4%EB%82%98-%EC%B0%A8%EB%A0%A4%EC%95%BC%EC%A7%80 ์ด๋ ๋ SNS ํผ๋์ ๊ฐ๋ฐ ๊ด๋ จ๋ ์์๋ค์ ๋ฐ์๋ณด๋ค๊ฐ ๊ฐ๋ฐ 7๋
์ฐจ. ๋งค๋์ 1์ผ์ฐจ๋ผ๋ ์ ๋ชฉ์ ์ฑ
์ ๋ณด๊ฒ ๋๋ค. ๋ญ์ผ, ์ด๊ฑฐ ๋ด ์ด์ผ๊ธฐ ์๋์ผ? ํ๋ฉฐ ๊ท์ ์ ํ๋ฆฐ ๋ฏ ์ฌ์ ์ฝ์ด๋ณด๋ ค๋ ์ฐฐ๋, ๋ง์นจ ํ๋น๋ฏธ๋์ด ์์ ์ฃผ์ตํ๋ ๋๋ ๋ฆฌ๋ทฐ์ด๋ค ๋ผ๋ ์ด๋ฒคํธ๋ฅผ ๋ฐ๊ฒฌํ๊ฒ ๋๋ค. ๊ฒฐ๊ตญ ๋ฆฌ๋ทฐ์ด์ ๋น์ฒจ์ด ๋๊ณ ์ด ์ข๊ฒ ํด๋น ์ฑ
์ ๋ฐ์๋ณผ ์ ์์๋ค. (์ด ์ฑ
์ ์ฝ๊ฒ ํด์ค ํ๋น๋ฏธ๋์ด ์ธก์๊ฒ ์ด ๊ธ๋ก๋๋ง ๊ฐ์ฌ์ ์ธ์ฌ๋ฅผ ์ ํ๊ณ ์ถ๋ค.)
ํ์์ SNS๋ฅผ ์ฅ์ํ๋ ‘๊ฐ๋ฐ 7๋
์ฐจ, ๋งค๋์ 1์ผ์ฐจ’" ํ์์ SNS๋ฅผ ์ฅ์ํ๋ ‘๊ฐ๋ฐ 7๋
์ฐจ, ๋งค๋์ 1์ผ์ฐจ’ ์ด๋ฒ ํฌ์คํ
์์๋ ์ฐ์ ์ฑ
์ ๋ํ ๋ฆฌ๋ทฐ๋ฅผ ๊ฐ๋จํ ์ ์ด๋ณด๊ณ ๊ฑฐ๊ธฐ์ ํ์์ ์๊ฐ์ ์กฐ๊ธ ๋ ์น์ด๋ณด๊ณ ์ถ๋ค. ํ์๋ฅผ ๋๊ณ ๋ง๋ค์ด์ง ์ฑ
๊ฐ์์ ์์ง๋ ์ฑ
ํ์ง๋ง ๋ด๋ ์ ๊ธฐํ๊ณ ์ค๋ ๋ค. ์ผ๋จ ์ฑ
ํ์ง๋ ์ ๋ชฉ์ด ๋ง์ ๋ ๊ฑด ๊ฐ์ถ ์ ์๋ ์ฌ์ค์ด๋ค.
์ ์
ํน์ ์ฃผ๋์ด ๊ฐ๋ฐ์๊ฐ ์ฝ์ด๋ด๋ ์ข์ ์ฑ
. ์ ๋ชฉ๋ง ๋ณด๋ฉด ์ด์ ๊ฐ ํ์ฅ ํน์ ๋งค๋์ ๋ฅผ ํ๊ฒ ๋๋ ์ฌ๋์๊ฒ๋ง ํด๋น๋๋ ์ฑ
์ผ๋ก ๋ณด์ธ๋ค. ํ์ง ์๋จ์ “๊ฐ๋ฐ๋ง ํด์๋ ๋ด๊ฐ, ์ด๋ ๋ ๊ฐ์๊ธฐ ‘ํ’์ ๋งก์๋ค!” ์ ํ์๊ธฐ๋ ํ์ผ๋๊น. ํ์ง๋ง ์ฑ
์ ์ฝ๋ค ๋ณด๋ฉด ๊ผญ ๊ทธ๋ ์ง๋ง๋ ์๋ค. ๋ฉํ ๋ง์ ํ ๋์ ๋ฉํ ์ ๋ฉํฐ ๊ฐ์์ ์์น์์ ์ด๋ค ์์ธ๋ก ์๋ก๋ฅผ ๋ง์ดํด์ผ ํ๋ ๋ฐฉ๋ฒ์ ๋ํด์๋ ์๋ ค์ฃผ๊ธฐ๋ ํ๊ณ ๋ฌด์์ ๋์์ ์๋ ๊ธฐ๋ฅ ๊ฐ๋ฐ๋ง์ ํ๋ฉฐ ์๊ฐฏ์์ ๊ฑท๋ ์ฃผ๋์ด ๊ฐ๋ฐ์๊ฐ ๋ฏธ๋ฆฌ ๋ฏธ๋๋ฅผ ๊ฒฝํํด๋ณด๋ ์ข์ ์ฌ๋ก๋ฅผ ๋ค์ด ์๋ ค์ฃผ๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ผญ ๋๊ตฐ๊ฐ ํน์ ๋ฌด์ธ๊ฐ๋ฅผ “๊ด๋ฆฌ"ํ๋ ์
์ฅ์ด ์๋ “ํ"์ด๋ผ๋ ๊ณต๋์ฒด ์ฌํ, ํนํ ๊ฐ๋ฐ ํ์์ ํ์๋ค๊ณผ ํ๋ ฅํ๋ ๋ฐฉ๋ฒ๋ก ์ ์ดํด๋ณด๊ณ ์๊ณ , ๊ฒฝ๋ ฅ์ด ๋ฎ์ผ๋ฉด ์ ๋ณด์ด๋ ๋ถ๋ถ๋ค๊น์ง ๋ง์น ๋ฉ๋ฆฌ ์๋ ๊ฒ์ ๋์ ๋ง์๊ฒฝ์ผ๋ก ๋ณด์ฌ์ฃผ๋ ๋๋์ด ๋ค์๋ค. ์๋ถ๋ถ์๋ “์ด ์ฑ
์ ์ฝ๋ ๋ฐฉ๋ฒ"์ด๋ผ๋ฉฐ ์ํฉ๋ณ๋ก ์ฝ๋ ์ฑํฐ๋ฅผ ๊ฐ์ด๋ ํด์ฃผ๊ณ ์์ง๋ง ์ฌ์ค ์ด๋ ํ๋ ์ค์ํ์ง ์์ ๋ด์ฉ์ด ์์ด์ ์ฒ์๋ถํฐ ๋ฌด์ธ๊ฐ์ ํ๋ฆฐ ๋ฏ ์ฝ์ ์๋ฐ์ ์์๊ณ ์ ๋ฐฐ๋์ด ์์ ์ง๋๊ฐ ๊ธธ์ ์ฌ๋ฐ๋ฅด๊ฒ ์ง๋๊ฐ ์ ์๋๋ก ๊ฐ์ด๋ ํด์ฃผ๋ ๋๋์ผ๋ก ์ค๊ฐ์ค๊ฐ ์ฌ๋ก๊ฐ ์์ด์ ํ์
์ ์์ด์ ๊ทธ๋ฐ์ง ์ข ๋ ์ฝ๊ฒ ์ฝํ ์ ์์๋ค.
๋ค ์ฝ๊ณ ์์ผ ์์์ฐจ๋ฆฐ ๋ฒ์ญ์(?)๋ผ๋ ์ฌ์ค. ์ด๋ ํ XX ๊ธฐ์ ์์ ์์๋ Method๋ฅผ ‘๋ฐฉ๋ฒ’, Overriding ์ ‘๊ณผ์ ’์ด๋ผ๊ณ ๋ฒ์ญํ ์ฑ
๋ค์ด ์๋๊ฐ ๋ฐ๋ฉด, ์ด ์ฑ
์ ์ฝ๋ ๋ด๋ด ๊ตญ๋ด ์ด๋ค ๋ถ์ด ์ฐ์ ๊ฑฐ๋ผ ์๊ฐํ๊ณ ์ฝ์ด๋ด๋ ค ๊ฐ์ง๋ง ๋ค ์ฝ๊ณ ๋ณด๋ ์ธ๊ตญ์ ์ด๋ CTO๊ฐ ์ด ์ฑ
์ ์ฎ๊ฒจ์ ๋ค์ ์จ์ง ์ฑ
์ด์๋ค.
์น ์ดํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค๋ฉด์ ๊ผญ ํ๋ฒ ์ฏค ๋ง๋๊ฒ ๋๋ “RestTemplate”. ์ ๊ทผ ๊ฐ๋ฅํ ์ธ๋ถ HTTP URL(๋ณดํต API)์ ํธ์ถํ๋ ๋ฐฉ๋ฒ์ค์ ํ๋๋ก springframework ์์ ์ ๊ณตํด์ฃผ๋ ๋ชจ๋์ด๋ค. ํนํ ํฐ ํ๋ฉ์ด๋ฆฌ๋ก ๊ด๋ฆฌ๋๋ Monolithic Architecture ์์ ์์ฒญ์ ํ๊ณ (client) ์๋ต์ ์ฃผ๋(server) ์ฆ, Endpoint๊ฐ ์์ ๋จ์๋ก ๋ถ๋ฆฌ๋๋ Microservice Architecture ๋ก ๋ฐ๋๋ฉด์ ๊ฐ ์๋น์ค๊ฐ ํธ์ถ๋ฐฉ์์ด HTTP ์ผ ๊ฒฝ์ฐ ์์ฃผ ์ฌ์ฉ๋๊ณค ํ๋ ๊ฒ ๊ฐ๋ค. (webClient ๋ฑ ๋ค๋ฅธ ์ฌ๋ฌ ํธ์ถ ๋ฐฉ๋ฒ๋ค์ด ์๋ค.) ๋ง์ฝ, ์์ฒญ์ ํ๋ ํด๋ผ์ด์ธํธ ์
์ฅ์์ ์๋ต์ ์ฃผ๋ ์๋ฒ์ ์ํ๊ฐ ๋ถ์์ ํ๋ค๊ณ ๊ฐ์ ํ์๋, ์ด๋ค์์ผ๋ก ์ฒ๋ฆฌํด์ผ ํ ๊น? ์์ปจ๋, ์์ฒญ 10๋ฒ์ ํ๋ฒ์ ์ด๋ ํ ์ด์๋ก ์๋ต์ด ์ง์ฐ๋๊ฑฐ๋ ์๋ฒ์๋ฌ๊ฐ ๋ฐ์ํ๋ค๊ณ ํ๋ฉด ํด๋ผ์ด์ธํธ๋ฅผ ์ฌ์ฉํ๋ ์ฌ์ฉ์ ์
์ฅ์์๋ ๊ฐํ์ ์ธ ์ค๋ฅ์๋ต์ ๋ต๋ตํจ์ ํธ์ํ ์๋ ์๋ค. ๊ทธ๋ผ ์ ์ ๋์ ๊ฐ๊ณ ์๊ฐํด๋ณด์. ๊ฐ๋ณ๊ฒ ์๊ฐํ๋ฉด ์๋์ฒ๋ผ ์์ฃผ ๊ฐ๋จํ๊ฒ “์์ธ์ฒ๋ฆฌ"๋ฅผ ์ด์ฉํ ์๋ ์๋ค.
try { // http call } catch (Exception e){ // ์๋ฒ์๋ฌ๊ฐ ์๋ ์ฝ์๋ ์๋ฌ์๋ต์ ๋ฆฌํด } ํ์ง๋ง ์ด๊ฒ๋ ์ ๋ต์ด ์๋์ ์๋๊ฒ, “๊ฐํ์ ์ธ ์ค๋ฅ"๋ก ์ธํด ์ฌ์ฉ์๋ ์ค๋ฅํ๋ฉด์ ๋ด์ผํ๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ์ ๋ํ ์ ๋ขฐ๋ฅผ ์ ๋ฒ๋ฆด ์๋ฐ์ ์๋ค. ๊ทธ๋ผ ์ด๋ป๊ฒ ํด์ผํ ๊น? ์ฌ๋ฌ๊ฐ์ง ํด๊ฒฐ๋ฐฉ๋ฒ์ด ์๊ฒ ์ง๋ง ๊ฐ๋จํ๋ฉด์๋ ๊ฐ๋ ฅํ๋ค๊ณ ์๊ฐ๋๋ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก “์ฌ์๋” ๋ผ๊ณ ์๊ฐํ๋ค. ํด๋ผ์ด์ธํธ๋ฅผ ์ฌ์ฉํ๋ ์ฌ์ฉ์๊ฐ ๋์น ๋ชป์ฑ๋งํผ ๋น ๋ฅด๊ฒ ์ฌ์๋๋ฅผ ํ๋ค๋ฉด ์๋ฌ๊ฐ ๋๋ ๋ค์ํ๋ฒ ํธ์ถํด์ ์ฑ๊ณตํ ์ ์๋ ๊ฐ๋ฅ์ฑ์ด ๋๊ธฐ ๋๋ฌธ์ด๋ค. (๊ทธ์น๋ง ๊ทผ๋ณธ์ ์ธ ์์ธ์ ํด๊ฒฐํด์ผ…)
์ค์ ๋ก ์กฐ๊ธ์๋ค ํด๋ณด๋ฉด ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ผ๋ ์๋ ๋๋ ์กฐ๊ธ (์ฒ์ฒํ) ์๋ํด๋ณด์. ์ถ์ฒ : http://www.segye.com/newsView/20200302504384" ์ค์ ๋ก ์กฐ๊ธ์๋ค ํด๋ณด๋ฉด ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ผ๋ ์๋ ๋๋ ์กฐ๊ธ (์ฒ์ฒํ) ์๋ํด๋ณด์. ์ถ์ฒ : http://www.segye.com/newsView/20200302504384 ์ด๋ฒ ํฌ์คํ
์์๋ RestTemplate ๋ฅผ ์ด์ฉํ ๋ “์ฌ์๋” ํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด๊ณ ์ ํ๋ค. ์์ฃผ ๊ฐ๋จํ ์ง ๋ชจ๋ฅด์ง๋ง ๋
ธ๋ ฅ์ ๋นํด ํจ๊ณผ๊ฐ ์๋นํ๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ ์ ๋ฆฌํด ๋๊ณ ์ถ์๋ค.
Spring Retry ๊ณต์ Github์ ์๊ฐ๋ฅผ ๋น๋ฆฌ์๋ฉด, Spring ์ดํ๋ฆฌ์ผ์ด์
์ ๋ํ ์ฌ์๋ ์ง์์ ์ ๊ณตํ๋ค๊ณ ํ๋ค. ์์์ ์ด์ผ๊ธฐ ํ๋ “RestTemplate"๊ณผ๋ ์ฌ์ค ๋ฌด๊ดํ๊ณ , ์ด๋ฅผ ํ์ฉํด์ ์ฌ์๋ ํ๋ “RetryRestTemplate"๋ฅผ ๊ตฌํํด๋ณด๋ ค ํ๋๊ฒ์ด๋ค. ์ฐ์ ์ด “Spring-Retry"์ ์์ ๋ฅผ ๋ณด๋ฉด ์์ฃผ ์ฌํํ๊ฒ ์ฌ์ฉํ ์ ์๋ค. ์ฐ์ pom์ ๊ตฌํ์ ํ์ํ dependency ๋ฅผ ์ถ๊ฐํ๊ณ ์๋ ์ฝ๋๋ฅผ ๋ณด์.
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> @Configuration @EnableRetry // 1 public class Application { @Bean public Service service() { return new Service(); } } @Service class Service { @Retryable(RemoteAccessException.class) // 2 public void service() { // ... do something } @Recover // 3 public void recover(RemoteAccessException e) { // ... panic } } @EnableRetry ์ด๋
ธํ
์ด์
์ @Configuration์ ์ง์ ํ ํด๋์ค ์ค ํ๋์ ์ถ๊ฐํ๋ค. ์ฌ์๋ ํ๋ ค๋ ๋ฉ์๋์ @Retryable ์ด๋
ธํ
์ด์
์ ์ง์ ํด์ค๋ค. ์ฌ์๋๊ฐ ์๋ฃ๋๋ ์์ ์์ ์คํํ๊ณ ์ถ์๋ ์ ์ธํ๋ ์ด๋
ธํ
์ด์
, @Retryable ๋์ผํ ํด๋์ค์์ ์ ์ธ๋์ด์ผ ํ๊ณ return type ์ @Retryable์ ์ง์ ํ ๋ฉ์๋์ ๋์ผํด์ผ ํ๋ค. Retry Rest Template ์ด๋ ๊ฒ springframework ์์ ์ ๊ณตํด์ฃผ๋ spring-retry ๋ฅผ ์ด์ฉํด์ ์ด๋ฒ ํฌ์คํ
์ ๋ชฉํ์ธ ์ฌ์๋๋ฅผ ํ๋ Retry Rest Template ๋ฅผ ๊ตฌ์ฑํด๋ณด์. ์ฐ์ , RestTemplate ๋ฅผ Bean ์ผ๋ก ๋ฑ๋กํ๊ณ , ์์์ ์ด์ผ๊ธฐ ํ ์ด๋
ธํ
์ด์
๋ค๋ก ๊ตฌ์ฑํด๋ณด์.
@EnableRetry @Configuration public class RetryableRestTemplateConfiguration { @Bean public RestTemplate retryableRestTemplate() { SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory(); // 1 clientHttpRequestFactory.setReadTimeout(2000); clientHttpRequestFactory.setConnectTimeout(500); RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory) { @Override @Retryable(value = RestClientException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000)) // 2 public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType) throws RestClientException { return super.exchange(url, method, requestEntity, responseType); } @Recover public <T> ResponseEntity<String> exchangeRecover(RestClientException e) { return ResponseEntity.badRequest().body("bad request T.T"); // 3 } }; return restTemplate; } } SimpleClientHttpRequestFactory ๋ฅผ ๋ง๋ค๊ณ ๊ฐ ํ์์์์ ์ค์ ํด์ค ๋ค์ RestTemplate ํ๋ผ๋ฏธํฐ๋ก ๋๊ฒจ์ค๋ค. ์ฌ์ฉํ๋ ๊ณณ์์ exchange ๋ฉ์๋๋ฅผ ์ด์ฉํ ๊ฒ์ด๋ฏ๋ก ํด๋น ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ ํด์ค๋ค.
API๋ฅผ ๊ฐ๋ฐํ๊ณ ์ ๊ณตํ๊ธฐ ์ํด์๋ ๊ทธ์ ํด๋นํ๋ API ๋ช
์ธ๋ฅผ ์์ฑํด์ ์ฌ์ฉํ๋ ๊ณณ์ ์ ๋ฌํ๊ฒ ๋๋ค. ์ด๋ค URL์ ์ด๋ค ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํด์ ์ด๋ป๊ฒ ์์ฒญ์ ํ๋ฉด ์ด๋ค ๊ฒฐ๊ณผ๋ฅผ ์๋ต์ผ๋ก ๋ด๋ ค์ฃผ๋์ง์ ๋ํ ๊ด๋ จ ์ ๋ณด๋ค. ์ด๋ฌํ “API ๋ฌธ์” ๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ์์ ์ํฉ์ ๋ฐ๋ผ ๋ค์ํ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉ๋๊ณค ํ๋ค. API ์ฝ๋์ ํด๋น ๋ฌธ์์ ๋๊ธฐํ๊ฐ ์๋์ผ๋ก ๋์ด์ผ ์กฐ๊ธ ํธํด์ง๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์๋ค. ์ถ์ฒ : https://dribbble.com/shots/3386291-API-Documentation" API ์ฝ๋์ ํด๋น ๋ฌธ์์ ๋๊ธฐํ๊ฐ ์๋์ผ๋ก ๋์ด์ผ ์กฐ๊ธ ํธํด์ง๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์๋ค. ์ถ์ฒ : https://dribbble.com/shots/3386291-API-Documentation ํ์๋ ์ฃผ๋ก “์ํค”(๋๋ ์ผ๋ฐ ๋ฌธ์)๋ฅผ ํ์ฉํด์ ์ ๋ฌํ๊ณค ํ์๋๋ฐ API์ ํํ๊ฐ ๋ฌ๋ผ์ง ๋๋ง๋ค ํด๋น ์ํค๋ฅผ ์์ ํด์ผ๋ง ํ๋ ๋ฒ๊ฑฐ๋ก์์ด ์์๋ค. API ์์ ํ๋ฉด ์ํค๋ ์์ ํ๊ณ . ๊น๋ฐํ๊ณ ์ํค ์์ ์ ์ํ๊ฒ ๋ ๊ฒฝ์ฐ ์ API ๋ช
์ธ๊ฐ ๋ค๋ฅด๋๋ ๋ฌธ์๊ฐ… ๊ทธ๋ฌ๋ค ์๊ฒ๋ Spring Rest Docs. (์๋ฌด๋ฆฌ ์ข์ ๊ธฐ์ , ์ข์ ํด ์ด๋ผ ํด๋ ์ค์ ๋ก ๋ณธ์ธ์ด ํ์๋ก ํ๊ณ ์ฌ์ฉ์ ํด์ผํ๋ ์ด์ ๊ฐ ์๊ธธ๋ ๋น๋ก์ ๋น์ ๋ฐํ๋๊ฒ ๊ฐ์ ๋๋์ด๋ค.)
์ด ํฌ์คํ
์์๋ swegger ์ ๋น๊ตํ๋ ๋ด์ฉ์ ์ ์ธํ ๊น ํ๋ค. ์๋ ์ ๋ช
ํ ๋ ์๋ ์ฐ๋งฅ(?)์ด๋ผ ๊ฒ์ํด๋ณด๋ฉด ๊ฐ๊ฐ์ ์ฅ๋จ์ ์ด ์์ธํ ๋์์๊ธฐ์…
์ต๊ทผ ๋ค์ด TestCode ์ ์ค์์ฑ์ ์ ์คํ๊ฒ ๋๋ผ๊ณ ์์๊ณ , TestCode ๋ฅผ ์์ฑํ๋ฉด ์์ฐ์ค๋ฝ๊ฒ ๋ฌธ์๋ฅผ ๋ง๋ค์ด ์ฃผ๋ ๋ถ๋ถ์ด ๊ฐ์ฅ ๋งค๋ ฅ์ ์ด๋ผ๊ณ ์๊ฐ์ด ๋ค์๋ค. ์ด๋ฅผ ๋ฐ๋๋ก ์๊ฐํ๋ฉด, TestCode ๊ฐ ์คํจํ ๊ฒฝ์ฐ ๋น๋ ์์ฒด๊ฐ ์๋๊ธฐ์ ์ด์ฉ์ ์์ด TestCode๋ฅผ ์ฑ๊ณต์์ผ์ผ๋ง ํ๊ณ , ์์ฐ์ค๋ฝ๊ฒ ์ ์์ ์ธ(์ต์ ํ ๋) API ๋ฌธ์๊ฐ ๋ง๋ค์ด์ง๊ฒ ๋๋ค.
์ด๋ฒ ํฌ์คํ
์์๋ ๋ค์๊ณผ ๊ฐ์ ๋ชฉํ๋ฅผ ๋๊ณ ์ค๋ฌด์์ ์ธ์ ๋ ์ง ํ์ฉ์ด ๊ฐ๋ฅํ ์ฝ๊ฐ์ “๊ฐ์ด๋” ๊ฐ์ ๋ด์ฉ์ผ๋ก ์์ฑํด ๋ณด๊ณ ์ ํ๋ค.
Spring Boot ์ต์ ๋ฒ์ ์์ Spring Rest Docs ๋ฅผ ์ค์ ํ๋ค. ์์์ API ๋ฅผ ๋ง๋ค๊ณ ๊ทธ์ ๋ฐ๋ฅธ TestCase ๋ฅผ ์์ฑํ๋ค. Spring.profile ์ ๋ฐ๋ผ Spring Rest Docs Url ์ ์ ๊ทผ ๊ฐ๋ฅ/๋ถ๊ฐ๋ฅ ํ ์ ์๊ฒ ํ๋ค. ๋ฌผ๋ก ํ์์ ๋ฐฉ๋ฒ์ด ๋ค๋ฅผ์๋ ์์ง๋ง, ์ด๋ฌํ ๋ฐฉ๋ฒ์ ํ ๋๋ก ๋ณด๋ค ๋ ์ฐ์ํ๊ณ ์๋ฆ๋ค์ด ๋ฐฉ๋ฒ์ ์์๊ฐ์ ์์ง ์์๊น ํ๋ ๊ธฐ๋๋ก.
Spring Boot ์ Spring Rest Docs ์
ํ
ํ๊ณ TestCase ์์ฑํ๊ธฐ ์ฐ์ Spring Boot ํ๋ก์ ํธ๋ฅผ ๋ง๋ ๋ค. https://start.spring.io/ ์์ ๋ง๋ค์ด๋ ๋๊ณ IDE ์์ ์ ๊ณตํ๋ ํด๋ก ๋ง๋ค์ด๋ ๋๊ณ . ๋ง๋๋ ๋ฐฉ์์ ๋ฌด๋ฐฉํ๋ค. ๊ทธ ๋ค์ ํ์ํ dependency ๋ฅผ ์ถ๊ฐํด ์ค๋ค.
<dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-mockmvc</artifactId> <scope>test</scope> </dependency> ์์๋ก API๋ฅผ ์์ฑํ๊ณ
๋ชจ๋ธ @Getter @Setter public class Book { private Integer id; private String title; private String author; } ์ปจํธ๋กค๋ฌ @RestController public class BookController { @GetMapping("/book/{id}") public Book getABook(@PathVariable Integer id) { Book book = new Book(); book.setId(id); book.setTitle("spring rest docs in spring boot"); book.setAuthor("taetaetae"); return book; } } ํด๋น ์ปจํธ๋กค๋ฌ์ ๋ํ TestCase ๋ฅผ ์์ฑํ์.
@WebMvcTest(BookController.class) @AutoConfigureRestDocs // (1) public class BookControllerTest { @Autowired private MockMvc mockMvc; // (2) @Test public void test_์ฑ
์_์กฐํํ๋ฉด_null์ด_์๋_๊ฐ์ฒด๋ฅผ_๋ฆฌํดํ๋ค() throws Exception { mockMvc.perform(get("/book/{id}", 1) .accept(MediaType.APPLICATION_JSON)) .andDo(MockMvcResultHandlers.print()) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(document("book", // (3) pathParameters( parameterWithName("id").description("book unique id") // (4) ), responseFields( fieldWithPath("id").description("book unique id"), fieldWithPath("title").description("title"), fieldWithPath("author").description("author") ) )) .andExpect(jsonPath("$.id", is(notNullValue()))) // (5) .andExpect(jsonPath("$.title", is(notNullValue()))) .andExpect(jsonPath("$.author", is(notNullValue()))); } } (1) Spring Boot ์์๋ ํด๋น ์ด๋
ธํ
์ด์
์ผ๋ก ์ฌ๋ฌ์ค์ ๊ฑธ์ณ ์ค์ ํด์ผ ํ Spring Rest Docs ๊ด๋ จ ์ค์ ์ ์์ฃผ ๊ฐ๋จํ๊ฒ ํด๊ฒฐํ ์ ์๊ฒ ๋๋ค. (์ฐธ๊ณ )
(2) ๊ณต์ ๋ํ๋จผํธ ์์๋ 4๊ฐ์ง ๋ฐฉ์์ ๋งํ๊ณ ์๋๋ฐ ์ด ํฌ์คํ
์์๋ “MockMvc” ์ ์ฌ์ฉํ๊ณ ์ ํ๋ค.
(3) “book” ์ด๋ผ๋ identifier ๋ฅผ ์ง์ ํ๋ฉด ํด๋น TestCase ๊ฐ ์ํ๋ ๋ snippets ๊ฐ ์์ฑ๋๋๋ฐ ํด๋น identifier ๋ฌถ์์ผ๋ก ์์ฑ์ด ๋๋ค.
(4) request์ ํ๋ผ๋ฏธํฐ ํ๋, response์ ํ๋์ ์ค๋ช
์ ์ ์ด์ค์ผ๋ก์จ ์ด ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ snippets ๊ฐ ์์ฑ์ด ๋๊ณ ๊ฒฐ๊ณผ์ ์ผ๋ก API ๋ฌธ์๊ฐ ๋ง๋ค์ด ์ง๋ค.
(5) ํ์๊ฐ ๊ฐ์ฅ ๋งค๋ ฅ์ ์ด๋ผ ์๊ฐ๋๋ ๋ถ๋ถ. ์ด ๋ถ๋ถ์์ ํ
์คํธ๋ฅผ ๋์์ ํจ์ผ๋ก์จ ์๋ต์ด ๋ฌ๋ผ์ง๊ฑฐ๋ ์๋ชป๋ ์๋ต์ด ๋ด๋ ค์ฌ ๊ฒฝ์ฐ TestCase๊ฐ ์คํจํ๊ฒ ๋์ด API๋ฌธ์ ๋ํ ์์ฑ๋์ง ์๊ฒ ๋๋ค.
ํ์ด์ฌ์ด๋ผ๋ ์ธ์ด๋ ๋ค๋ฅธ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ค์ ๋นํด ์ฝ๊ณ ์ง๊ด์ ์ด๋ผ ๊ทธ๋ฐ์ง ํ๋ก๊ทธ๋๋ฐ์ ์ฒ์ ์์ํ๋ ์ฌ๋๋ค์๊ฒ ๋์ฑ์ด ์ฃผ๋ชฉ์ ๋ฐ๊ณ ์๋๊ฒ ๊ฐ๋ค. ์ ๋ง ๋ค์ํ ๋ชจ๋๋ค์ด ๋ง์ ์ฌ๋ฌ๋ถ์ผ์์ ํ์ฉ๋๊ณ ์๊ณ ํนํ ์ธ์ ๋ถํฐ์ธ๊ฐ ํซ! ํด์ง ๋ถ์ผ(?)๋ผ ํด๋ ๊ณผ์ธ์ด ์๋์ ๋์ธ “๋จธ์ ๋ฌ๋” ๋ถ์ผ์์๋ ๋ค์ํ๊ฒ ์ฌ์ฉ๋๊ณ ์๋๊ฒ ๊ฐ๋ค.
๋ง์นจ ํ์๊ฐ ์ํด ์๋ ํ ๋ด์ ๋จธ์ ๋ฌ๋ ์คํฐ๋๊ฐ ์์์ด ๋์๊ณ , ๊ทธ์ ํ์ด์ฌ์ ์ด์ฉํ์ฌ ์คํฐ๋๋ฅผ ํด์ผํ๋ ์ํฉ. ํ์ง๋ง ์คํฐ๋๋ฅผ ํ๋ ํ์ ์ ๋ฐ ์ด์์ด ํ์ด์ฌ์ ์ด์ฉํ ๊ฐ๋ฐ ๊ฒฝํ์ด ์์๊ณ , ์๋ก ๋ฐฐ์ด๊ฒ์ ๊ณต์ ๋ฅผ ํ๋ฉด์ ์คํฐ๋๋ฅผ ํ๋ฉด ๋ ์ข๊ฒ ๋ค๋ ์๊ฐ์ด ๋ค๋ ์ฆ์. ์ธ์ ์ด๋์ ๊ฐ ๋ดค๋๊ฒ์ด ๋จธ๋ฆฟ์์ ์ค์ณ ์ง๋๊ฐ๋ค. ๊ทธ๊ฑด ๋ฐ๋ก Jupyter(์ดํ ์ฃผํผํฐ).
์ถ์ฒ : https://jupyter.org/" ์ถ์ฒ : https://jupyter.org/ ์ฃผํผํฐ๋ ์์ญ ๊ฐ์ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ๋ํ ํ ์ปดํจํ
์์ํ ์คํ ์์ค ์ํํธ์จ์ด, ์คํ ํ์ค ๋ฐ ์๋น์ค๋ฅผ ๊ฐ๋ฐํ๊ธฐ ์ํ ํด์ด๋ผ๊ณ ํ๋ค. ์ด ํฌ์คํธ๋ฅผ ์์ฑํ๊ธฐ ์ ๊น์ง๋ง ํด๋ “์ฃผํผํฐ == ํ์ด์ฌ ์น ๊ฐ๋ฐํด” ์ด๋ผ๊ณ ๋ง ์๊ณ ์์๋๋ฐ ์ข๋ ์ฐพ์๋ณด๋ ๋ค์ํ ์ธ์ด๋ฅผ ์ง์ํ๋๊ฒ ๊ฐ๋ค.
๊ทธ๋ผ ์ด๋ฌํ ์ฃผํผํฐ๋ฅผ ํน์ ์๋ฒ์ ์ค์นํ๊ณ ๋ก์ปฌ์ ํ์ด์ฌ์ ์ค์นํ์ง ์์๋ ์๊ฒฉ์ผ๋ก ํ์ด์ฌ ์ฝ๋ฉ์ ํด๋ณด๋ฉด ์ข๋ ์คํฐ๋์ ๋์์ด ๋์ง ์์๊น ํ๋ ๋ง์์ด ๋ค์๋ค. ๋ํ ํ๊ต์์ ์ด๋์ฅ์ ์๋๋ฅผ ๊น์์ ๋ง๊ป ๋ฐ๋์ ์๊ฒ ํ๋ ๋๋์ผ๋ก ํ์๋ค์ ์ํด ์ค์น๋ฅผ ํด๋๊ณ ์๊ฒฉ์ผ๋ก ์ ์ํ ์ ์๊ฒ ํด๋๋ฉด ๋ชจ๋๊ฐ ํธํ๊ณ ์ฝ๊ฒ ํ์ด์ฌ์ ๋ํด ๊ฒฝํ์ ํด๋ณผ ์ ์์ง ์์๊น ํ๋ ๋ง์์ผ๋ก ์ฃผํผํฐ๋ฅผ ์ค์น๋ฅผ ํด ๋ณด๊ณ ์ ํ๋ค.
๋ณธ ํฌ์คํ
์ ๋ชฉํ๋ ๋ค์๊ณผ ๊ฐ๋ค.
ํ๊ฒฝ : CentOS 7.4 64Bit, python 2.7 (๊ธฐ๋ณธ) ๋ชฉํ anaconda ๋ฅผ ํ์ฉํ์ฌ ์์คํ
๊ธฐ๋ณธ ํ์ด์ฌ์ ๊ฑด๋๋ฆฌ์ง ์๋ ๊ฐ์ํ๊ฒฝ์ ๊ตฌ์ถํ๋ค. ์ฃผํผํฐ๋ฅผ ์ค์นํ๊ณ ์๊ฒฉ์ผ๋ก ์ ์ํ ์ ์๋๋ก ์ค์ ํ๋ค. ์ฌ๊ธฐ๊น์ง ๋ณด๋ฉด ํ์๊ฐ ์์ฒญ๋๊ฒ ํ์ด์ฌ์ ๋ํด ์ ์๋๊ฒ์ฒ๋ผ ๋ณด์ผ์๋ ์์ด ๋ฏธ๋ฆฌ ๋งํ์ง๋ง ํ์๋ ์ฐ ์๋ฐ ๊ฐ๋ฐ์์ด๋ฉด์ ํ์ด์ฌ ๊ฐ๋ฐ ์์ค์ ๊ธฐ๋ณธ์ ์ธ ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ๋ ์ ๋์ด๋ค. ๊ทธ๋ฌ๋ ์ด ํฌ์คํธ๋ฅผ ์ฝ๊ณ ์๋ ํ์๊ฐ์ ํ์๋ชป(?) ๋ถ๋ค๋ ์ถฉ๋ถํ ์ค์น๊ฐ ๊ฐ๋ฅํ๋ค. (์ต๋ํ ๋ฐ๋ผํ ์ ์์ ์ ๋์ ์นํธํค ์์ค์ผ๋ก ์์ฑ ํ๊ณ ์ ํ๋ค.)
์๋์ฝ๋ค ์ค์น (๋ค์ผ๋ก ์ค์น๋๋ ์ฃผํผํฐ) ์ฐ์ ์๋์ฝ๋ค๋ฅผ ์ค์นํ์. ์๋์ฝ๋ค๋ Anaconda(์ด์ : Continuum Analytics)๋ผ๋ ๊ณณ์์ ๋ง๋ ํ์ด์ฌ ๋ฐฐํฌํ์ผ๋ก, ์๋ฐฑ ๊ฐ์ ํ์ด์ฌ ํจํค์ง๋ฅผ ํฌํจํ๊ณ ์๋ค๊ณ ํ๋ค. ์ฆ, ์๋์ฝ๋ค๋ฅผ ์ค์นํ๊ณ ๋ง๋ค์ด์ง ๊ฐ์ํ๊ฒฝ์์ ํ์ด์ฌ ๊ฐ๋ฐ์ ํ๋ฉด ๋ค์ํ ๋ชจ๋์ด ์ด๋ฏธ ์ค์น๋์ด ์๊ธฐ ๋๋ฌธ์ ํธ๋ฆฌํ๋ค๋ ์ด์ผ๊ธฐ.
์ถ์ฒ : https://www.anaconda.com/" ์ถ์ฒ : https://www.anaconda.com/ ๋๋ถ์ด ์์คํ
์ ๊ธฐ๋ณธ์ผ๋ก ์ค์น๋์ด ์๋ ํ์ด์ฌ์ ๊ฑด๋๋ฆฌ๋ฉด ์ฌ๋ฌ ๋ณต์กํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ์. ์๋์ฝ๋ค๋ฅผ ํ์ฉํ์ฌ ํ์ด์ฌ 3์ ์ฌ์ฉํ๋ ๊ฐ์ํ๊ฒฝ์ ๋ง๋ค์ด ๋ณด์. ์ค์น๋ ์์ฃผ ๊ฐ๋จํ๋ค. ์๋์ฝ๋ค ์ค์นํ์ผ์ ๋ค์ด๋ฐ๊ณ ์ด๋ฅผ ์คํํ๋ฉด ๋. (user ๋ ๋ฒจ์ด root ๋ฉด sudo ๋ช
๋ น์ด๋ฅผ ์๋ตํด๋ ๋๋ค.)
$ wget https://repo.anaconda.com/archive/Anaconda3-2019.10-Linux-x86_64.sh $ sudo bash Anaconda3-2019.10-Linux-x86_64.sh Welcome to Anaconda3 2019.10 In order to continue the installation process, please review the license agreement. Please, press ENTER to continue >>> =================================== Anaconda End User License Agreement =================================== Copyright 2015, Anaconda, Inc. ~~~ ์ค๋ต ~~~ Do you accept the license terms? [yes|no] [no] >>> yes # yes!! Anaconda3 will now be installed into this location: /root/anaconda3 - Press ENTER to confirm the location - Press CTRL-C to abort the installation - Or specify a different location below [/root/anaconda3] >>> /home/anaconda3 # ์ค์น๋ ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ฃผ๊ณ ๊ธฐ๋ณธ ์ค์ ๊ฐ์ ์ค์นํ๋ ค๋ฉด ๊ทธ๋ฅ ์ํฐ ~~~๋ญ๊ฐ ์์ฒญ ์ค์น๋๋ค. ๋ฌผ ํ์ ๋จน๊ณ ์ค์.~~~ installation finished. Do you wish the installer to initialize Anaconda3 by running conda init? [yes|no] [no] >>> yes # yes!! ์ด๋ ๊ฒ ๋๋ฉด ์ค์น๋ ๋. ํ๊ฒฝ๋ณ์๋ฅผ ์ค์ ํด์ ๊ธฐ๋ณธ ํ์ด์ฌ ํ๊ฒฝ์ ์๋์ฝ๋ค์ ์ํด ์ค์ ๋๋๋ก ๋ง์ถฐ์ฃผ์.
sudo vi .bashrc __conda_setup="$('/home/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)" if [ $? -eq 0 ]; then eval "$__conda_setup" else if [ -f "/home/anaconda3/etc/profile.