์๋ฐ์คํฌ๋ฆฝํธ๋ ๋ธ๋ผ์ฐ์ , Node.js๋ฅผ ํตํ ์๋ฒ, React Native๋ฅผ ํตํ ๋ชจ๋ฐ์ผ ์ฑ, ํด๋ผ์ฐ๋ ํจ์, ์ฃ์ง ์ปดํจํ ํ๊ฒฝ ๋ฑ ์ด๋์์๋ ์คํ๋๋ ์ ์ผํ ์ธ์ด์ ๋๋ค. ํ์ง๋ง ์ด๋ฌํ ๋ณดํธ์ฑ์๋ ํ์ง ์ ํ๋ผ๋ ๋๊ฐ๊ฐ ๋ฐ๋ฆ ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ์ ๋์ ํ์ดํ, ํ๋กํ ํ์ ์ฒด์ธ, ๋น๋๊ธฐ ์คํ ๋ชจ๋ธ ๋๋ถ์ ์ ์์ ์ธ ์ํฉ์์๋ ์ ์๋ํ๊ณ , ์ํฉ์ด ๋ณํ ๋๋ ๋ฏธ๋ฌํ ์ค๋ฅ๋ฅผ ๋ฐ์์ํค๋ ์ฝ๋๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์์ต๋๋ค. TypeScript๋ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ํฐ ๋์์ด ๋์ง๋ง, ํ์ ์์ ์ฑ์ด ๊ณง ์ฝ๋ ํ์ง, ๋ณด์ ๋๋ ์ํคํ ์ฒ ๊ฑด์ ์ฑ๊ณผ ๊ฐ์ ๊ฒ์ ์๋๋๋ค. ๋ฐ๋ก ์ด ๊ฐ๊ทน์ ์ ์ ๋ถ์์ด ๋ฉ์์ค๋๋ค.
JavaScript ๋๋ TypeScript ํ๋ก์ ํธ์ ์ ํฉํ ์ ์ ๋ถ์ ๋๊ตฌ ์กฐํฉ์ ์ ํํ๋ ๊ฒ์ ๊ฐ๋จํ ๊ฒฐ์ ์ด ์๋๋๋ค. ๋ฆฐํ , ๋ณด์ ๊ฒ์ฌ, ํ์ ๊ฒ์ฌ, ๋ฐ๋ ์ฝ๋ ๊ฐ์ง, ์ํคํ ์ฒ ๋ถ์์ ๊ฐ๊ฐ ๋ค๋ฅธ ๋๊ตฌ ๋ฒ์ฃผ์์ ๋ค๋ฃจ๋ ๋ณ๊ฐ์ ๋ฌธ์ ์ ๋๋ค. ๋ณด์ ์ค์บ๋๊ฐ ํ์ํ ๊ณณ์ ๋ฆฐํฐ๋ฅผ ์ฌ์ฉํ๊ฑฐ๋, ์์กด์ฑ ๋ถ์์ด ํ์ํ ๊ณณ์ ํ์ ๊ฒ์ฌ๋ง ์ฌ์ฉํ๋ ๊ฒ์ ๋ถ์์ ํ ๋ถ์ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํ๊ณ ์๋ชป๋ ํ์ ์ ์ฌ์ด์ค ์ ์์ต๋๋ค. ์ด ๊ฐ์ด๋์์๋ ๊ฐ ๋๊ตฌ๊ฐ ์ค์ ๋ก ์ํํ๋ ๊ธฐ๋ฅ๋ณ๋ก ์ ๋ฆฌํ์ฌ, ํ์์ ์ค๋ณต ์์ด ๋ชจ๋ ํ์ง ์ธก๋ฉด์ ํฌ๊ดํ๋ ๋๊ตฌ ์คํ์ ๊ตฌ์ถํ ์ ์๋๋ก ๋์ต๋๋ค.
๋ฐฉ๋ฒ SMART TS XL ์ํฐํ๋ผ์ด์ฆ ๊ท๋ชจ์์ JavaScript ์ ์ ๋ถ์์ ์ง์ํฉ๋๋ค.
์ด ๊ฐ์ด๋์์ ๋ค๋ฃจ๋ ๋ชจ๋ ๋๊ตฌ๋ JavaScript ๊ฒฝ๊ณ ๋ด์์ ์๋ํฉ๋๋ค. ESLint๋ JavaScript ํ์ผ์ ๋ถ์ํ๊ณ , TypeScript๋ TypeScript ํ๋ก์ ํธ ๋ด์ ํ์ ์ ๊ฒ์ฌํ๋ฉฐ, Semgrep์ JavaScript ๋ฐ TypeScript ์์ค ์ฝ๋์์ ์ทจ์ฝ์ ํจํด์ ์ค์บํ๊ณ , SonarQube๋ JavaScript ์ฝ๋๋ฒ ์ด์ค ์ ๋ฐ์ ํ์ง ์งํ๋ฅผ ์ถ์ ํฉ๋๋ค. ์ด๋ฌํ ๋๊ตฌ๋ค์ JavaScript ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ๊ณ๋ฅผ ๋์ด ํด๋น ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์กดํ๋ ์์คํ ์ด๋ ๊ทธ ์ ํ๋ฆฌ์ผ์ด์ ์ ์์กดํ๋ ์์คํ ์ ๋ค์ฌ๋ค๋ณผ ์๋ ์์ต๋๋ค.
SMART TS XL ์ ์ ๋ถ์์ ์ ๋ฐ๋ ๋ฐฉํฅ์์ ์ ๊ทผํฉ๋๋ค. ์ ์ฒด ์์คํ ์์ ์์ํ์ฌ ๊ตฌ์ฑ ์์ ์์ค๊น์ง ๊ตฌ์ถํด ๋๊ฐ๋๋ค. JavaScript์ ๊ฒฝ์ฐ, ์ด๋ JavaScript ๋ฐ TypeScript ์์ค ์ฝ๋๋ฟ๋ง ์๋๋ผ COBOL, JCL, Java, Python, RPG, PL/I, SQL ๋ฑ ํ๊ฒฝ์ ์๋ ๋ค๋ฅธ ๋ชจ๋ ์ธ์ด์ ์์ค ์ฝ๋๋ฅผ ์์งํ์ฌ ๋ชจ๋ ์ธ์ด ๊ฐ์ ๊ตฌ์กฐ์ ๊ด๊ณ๋ฅผ ๋ํ๋ด๋ ํตํฉ๋ ์ํธ ์ฐธ์กฐ ๋ชจ๋ธ์ ๊ตฌ์ถํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์๋ฅผ ๋ค์ด, JavaScript ๋ชจ๋์ด REST API๋ฅผ ํธ์ถํ๊ณ , ํด๋น API๋ Java ์๋น์ค๋ก ์ง์๋๋ฉฐ, ์ด ์๋น์ค๋ COBOL ๋ฐฐ์น ํ๋ก๊ทธ๋จ์ผ๋ก ์ฑ์์ง DB2 ํ ์ด๋ธ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ต๋๋ค. SMART TS XL ์ด ๊ทธ๋ฆผ์ ๋ค ๊ฐ์ ๋ ์ด์ด์ ๊ทธ ๋ ์ด์ด๋ค ์ฌ์ด์ ์ฐ๊ฒฐ ๊ด๊ณ๋ฅผ ๋ชจ๋ ๋ณด์ฌ์ค๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ๊ธฐ๋ฐ์ ์ด๋ค ๋๊ตฌ๋ ์ด๋ฌํ ๊ทธ๋ฆผ์ ์์ฑํ ์ ์์ต๋๋ค.

ํนํ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ๋ฐํ์ ๊ฒฝ์ฐ, SMART TS XL ๋ณดํธ๋ผ๊ธฐ ๊ฒ์ฌ ๋ฐ ๋ณด์ ๊ฒ์ฌ ๊ธฐ๋ฅ์ ๋ณด์ํ๋ ์ฌ๋ฌ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
์ธ์ด ๊ฐ ์ํฅ ๋ถ์. ๊ธฐ์ ์ฉ API๋ฅผ ์ฌ์ฉํ๋ JavaScript ๋ชจ๋์ ์์ ํ๊ธฐ ์ ์, SMART TS XL์ ์ํฅ ๋ถ์ ์ด ๊ธฐ๋ฅ์ ๋ค๋ฅธ ์ธ์ด๋ก ์์ฑ๋ ๊ตฌ์ฑ ์์๋ฅผ ํฌํจํ์ฌ ๋ณ๊ฒฝ ์ฌํญ์ ์ํฅ์ ๋ฐ๋ ์์คํ ๋ด์ ๋ชจ๋ ๊ตฌ์ฑ ์์๋ฅผ ์๋ณํฉ๋๋ค. ํ์ ๋ณ๊ฒฝ์ด ์คํ๋๊ธฐ ์ ์ ๋ณ๊ฒฝ์ ์ค์ ๋ฒ์๋ฅผ ํ์ ํ ์ ์์ผ๋ฉฐ, ํ๋ก๋์ ํ๊ฒฝ์์ ์์์น ๋ชปํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ํ์ ํ์ ํ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
์์คํ ์์ค์์์ ๋ฐ๋ ์ฝ๋ ๋ฐ ๋๋ฌ ๊ฐ๋ฅ์ฑ ๋ถ์. Knip๊ณผ ts-prune์ด JavaScript ํ๋ก์ ํธ ๋ด์์ ์ฌ์ฉ๋์ง ์๋ ๋ด๋ณด๋ด๊ธฐ๋ฅผ ์ฐพ๋ ์์น, SMART TS XL ์ด ๋๊ตฌ๋ ์์คํ ๋ด ์ด๋์์๋ ํธ์ถ๋์ง ์๋ JavaScript ํจ์ ๋ฐ ๋ชจ๋์ ์๋ณํ ์ ์์ต๋๋ค. ์ฌ๊ธฐ์๋ Java ์๋น์ค, ๋ฐฑ์๋ API ๋๋ ๋ฉ์ธํ๋ ์ ํ๋ก๊ทธ๋จ์ ํธ์ถ๋ ํฌํจ๋ฉ๋๋ค. ์ด๋ฌํ ์์คํ ์์ค์ ๋ฐ๋ ์ฝ๋ ๋ถ์์ JavaScript ํ๋ฐํธ์๋๊ฐ ๋ค๋ฅธ ์ธ์ด๋ก ์์ฑ๋ ๋ฐฑ์๋์ ๊ธด๋ฐํ๊ฒ ํตํฉ๋ ์กฐ์ง์์ ํนํ ์ ์ฉํฉ๋๋ค.
์ธ์ด ๊ฒฝ๊ณ๋ฅผ ๋๋๋๋ ์์กด์ฑ ์๊ฐํ. SMART TS XL์ ์ฝ๋ ์๊ฐํ ์ด ๋๊ตฌ๋ JavaScript ๋ชจ๋์ด Java ์๋น์ค, COBOL ํ๋ก๊ทธ๋จ, ๊ณต์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ ์ธ๋ถ API์ ์ด๋ป๊ฒ ์ฐ๊ฒฐ๋๋์ง ๋ณด์ฌ์ฃผ๋ ์ข ์์ฑ ๋งต์ ์์ฑํ๋ฉฐ, ์ธ์ด๋ณ ๊ฐ๋ณ ๋ณด๊ธฐ ๋์ ํ์ ๊ฐ๋ฅํ ๋จ์ผ ๋ค์ด์ด๊ทธ๋จ์ผ๋ก ์ ๊ณตํฉ๋๋ค.
์ด๊ธฐ์ข ์คํ์ ์ํ ํตํฉ ํ์ง ์ธก์ ๊ธฐ์ค. ์ฝ๋ ํ์ง ์งํ๋ฅผ ๊ฒฝ์์ง์ด๋ ๊ท์ ์ค์ ํ์ ๋ณด๊ณ ํ๋ ์กฐ์ง์ ์๋ฐ์คํฌ๋ฆฝํธ ๊ณ์ธต๋ฟ๋ง ์๋๋ผ ์ ์ฒด ์คํ์ ํฌ๊ดํ๋ ์งํ๋ฅผ ํ์ฉํ๋ฉด ์ด์ ์ ์ป์ ์ ์์ต๋๋ค. SMART TS XL์ ์ ์ ์ฝ๋ ๋ถ์ ์ด ํ๊ฐ ๊ธฐ์ค์ JavaScript์ TypeScript๋ฅผ ๋์ผํ ํ์ง ๊ธฐ์ค(์ํ ๋ณต์ก๋, ์ ์ง๋ณด์์ฑ ์ง์, ์์กด์ฑ ๊ฒฐํฉ๋)์ผ๋ก ํ๊ฐํ๋ฉฐ, ํ๊ฒฝ ๋ด ๋ชจ๋ ์ธ์ด์ ์ผ๊ด๋๊ฒ ์ ์ฉ๋ฉ๋๋ค.
๋ ๋ฆฝ์ ์ผ๋ก JavaScript ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ๋ ํ์ ๊ฒฝ์ฐ, ์ด ๊ฐ์ด๋์ ์๊ฐ๋ ์คํ ์์ค ๋ฐ ์์ฉ ๋๊ตฌ๋ ํฌ๊ด์ ์ธ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ๋ ํฐ ์ํฐํ๋ผ์ด์ฆ ์์คํ ์ ๊ตฌ์ฑ ์์๋ก JavaScript ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ๋ ํ์ ๊ฒฝ์ฐ, SMART TS XL ์ด๋ ํ์ผ ์์ค์ด ์๋ ์์คํ ์์ค์์ ๋๋จธ์ง ๋ถ์์ ์คํํ ์ ์๋๋ก ํ๋ ์ํคํ ์ฒ ๊ฐ์์ฑ ๊ณ์ธต์ ์ ๊ณตํฉ๋๋ค.
๋จผ์ง ๋ถ์๊ณผ ์ ์ ๊ธฐ ๋ถ์์ ์ฐจ์ด์ ์ ๋ฌด์์ผ๊น์?
์ด ์ฉ์ด๋ค์ ์ข ์ข ํผ์ฉ๋์ง๋ง, ์๋ก ๋ค๋ฅธ ์์ค์ ๋ถ์์ ๋ํ๋ ๋๋ค. ์ด๋ฌํ ๊ตฌ๋ถ์ ๋๊ตฌ ์ ํ์ ์์ด ์ค์ํฉ๋๋ค.
๋ณดํ ์คํ์ผ ์ผ๊ด์ฑ, ์ผ๋ฐ์ ์ธ ์ค๋ฅ ํจํด ๋ฐ ์ฝ๋ฉ ๊ท์น ์ค์์ ์ค์ ์ ๋ ์ ์ ๋ถ์์ ํ์ ์งํฉ์
๋๋ค. ๋ฆฐํฐ๋ ์์ค ์ฝ๋๋ฅผ ์ฝ๊ณ ์ ์๋ ๊ท์น ์งํฉ์์ ๋ฒ์ด๋ ๋ถ๋ถ์ ํ์ํฉ๋๋ค. ESLint๋ ๋ฆฐํฐ์ ํ ์ข
๋ฅ์ด๋ฉฐ, Biome์ ๋ฆฐํฐ ํฌ๋งทํฐ์
๋๋ค. ์ด๋ค์ ์คํ์ผ ์ผ๊ด์ฑ, ์ผ๋ฐ์ ์ธ ์ค๋ฅ ํจํด ๋ฐ ์ฝ๋ฉ ๊ท์น ์ค์ ์ฌ๋ถ๋ฅผ ํ์ธํฉ๋๋ค. no-unused-vars, no-console์์ฐ ๋ฐ prefer-const ์๋ฐ ์ฌํญ์ด ์์ต๋๋ค. ํจ์ ํธ์ถ ๊ฐ์ ๋ฐ์ดํฐ ํ๋ฆ์ ์ถ์ ํ์ง ์์ผ๋ฉฐ SQL ์ธ์ ์
๊ณผ ๊ฐ์ ๋ณด์ ์ทจ์ฝ์ ์ ์ฐพ์๋ด์ง๋ ์์ต๋๋ค.
๋ ๋์ ์๋ฏธ์ ์ ์ ๋ถ์์ ๋ฆฐํฐ๊ฐ ์ํํ๋ ๋ชจ๋ ๊ธฐ๋ฅ์ ๋ํ์ฌ ์ ์ด ํ๋ฆ ๋ถ์, ๋ฐ์ดํฐ ํ๋ฆ(์ค์ผ) ๋ถ์, ํธ์ถ ๊ทธ๋ํ ๊ตฌ์ฑ, ํ์ ์์ค ์ถ๋ก , ํ์ผ ๋ฐ ๋ชจ๋ ๊ฐ์ ํ๋ก์์ ๊ฐ ๋ถ์๊ณผ ๊ฐ์ ์ฌ์ธต์ ์ธ ๋ถ์์ ํฌํจํฉ๋๋ค. CodeQL, ์ค์ผ ๋ชจ๋๊ฐ ์ ์ฉ๋ Semgrep, SonarQube์ ๊ฐ์ ๋๊ตฌ๋ ์ด๋ฌํ ํฌ๊ด์ ์ธ ์๋ฏธ์ ์ ์ ๋ถ์์ ์ํํฉ๋๋ค. ์ด๋ฌํ ๋๊ตฌ๋ค์ ๋จ์ํ ๋ณ์๊ฐ ์ ์ธ๋์๋์ง ์ฌ๋ถ๋ง์ด ์๋๋ผ, ์ ๋ขฐํ ์ ์๋ ๋ฐ์ดํฐ๊ฐ ํ๋ก๊ทธ๋จ ๋ด์์ ์ด๋ป๊ฒ ์ด๋ํ๋์ง ์ดํดํจ์ผ๋ก์จ ์ทจ์ฝ์ ์ ์ฐพ์๋ ๋๋ค.
| ์นดํ ๊ณ ๋ฆฌ | ์ฐพ๊ธฐ | ๋ํ์ ์ธ ๋๊ตฌ |
|---|---|---|
| ๋ณดํ | ์คํ์ผ, ๊ด๋ก, ํํ ์ค์ | ESLint, Biome, OxcLint, StandardJS |
| ์ ํ ํ์ธ | ํ์ ์ค๋ฅ, ๋๋ฝ๋ ํ์ , ํ์ ๋ถ์ผ์น | ํ์ ์คํฌ๋ฆฝํธ(TSC), typescript-eslint |
| SAST/๋ณด์ ์ค์บ | SQL ์ธ์ ์ , XSS, ํ๋กํ ํ์ ์ค์ผ, ๋ถ์์ ํ ์ข ์์ฑ | Semgrep, CodeQL, Snyk Code, SonarQube |
| ์ฃฝ์ ์ฝ๋ ๊ฐ์ง | ์ฌ์ฉ๋์ง ์๋ ๋ด๋ณด๋ด๊ธฐ, ์ ๊ทผํ ์ ์๋ ์ฝ๋, ์ฌ์ฉ๋์ง ์๋ ๋ณ์ | Knip, ts-prune, ESLint no-unused-vars |
| ๊ฑด์ถ ๋ถ์ | ์์กด์ฑ ๋งคํ, ์ํฅ ๋ถ์, ํธ์ถ ๊ทธ๋ํ | SMART TS XL์ฝ๋์ฌ, ์์คํธ๋ ์ผ |
๋ชจ๋ ์์ฑ๋ ๋์ ์๋ฐ์คํฌ๋ฆฝํธ ํ๋ก์ ํธ๋ ์ต์ํ ์ฒ์ ์ธ ๊ฐ์ง ๋ฒ์ฃผ๋ฅผ ํฌํจํด์ผ ํฉ๋๋ค. ๊ท๋ชจ๊ฐ ํฌ๊ฑฐ๋ ์ํฐํ๋ผ์ด์ฆ๊ธ ํ๋ก์ ํธ๋ ๋ค์ฏ ๊ฐ์ง ๋ฒ์ฃผ ๋ชจ๋๋ฅผ ํฌํจํด์ผ ํฉ๋๋ค.
ESLint: ์๋ฐ์คํฌ๋ฆฝํธ ๋ฆฐํ ์ ์ํ ์ ๊ณ ํ์ค ๋๊ตฌ
ESLint๋ ๊ฑฐ์ ๋ชจ๋ JavaScript ํ๋ก์ ํธ์ ์ค์น๋์ด ์์ต๋๋ค. create-react-app, Next.js, Vite ๋ฐ ๋๋ถ๋ถ์ ์ํฐํ๋ผ์ด์ฆ ์ค์บํด๋ฉ์์ ๊ธฐ๋ณธ ๋ฆฐํฐ๋ก ์ฌ์ฉ๋ฉ๋๋ค. ํ๋ฌ๊ทธ์ธ ์ํ๊ณ๋ ๋ชจ๋ ์ฃผ์ ํ๋ ์์ํฌ(React, Vue, Angular, Node.js)์ ์ธ์ด ํ์ฅ(TypeScript)์ ์ง์ํฉ๋๋ค. ๋ฐ๋ผ์ ESLint๋ฅผ ์ ๋๋ก ์ดํดํ๋ ๊ฒ์ JavaScript ๊ฐ๋ฐ์ ํ์ ์กฐ๊ฑด์ ๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
# Install ESLint
npm init @eslint/config@latest
# Run on the project
npx eslint src/
# Auto-fix fixable issues
npx eslint src/ --fix
ESLint v9 ๋ฐ ํ๋ซ ์ค์ : ESLint v9์ด ์ด์ ๋ฒ์ ์ ๋์ฒดํ์ต๋๋ค. .eslintrc.* ํ๋ฉด์ ์ฌ์ฉํ ๊ตฌ์ฑ ํ์ eslint.config.js ์ด ํ์ผ์ ํธํ์ฑ์ ๊นจ๋จ๋ฆฌ๋ ๋ณ๊ฒฝ ์ฌํญ์ผ๋ก, ๊ธฐ์กด์ ๋ง์ ํ๋ก์ ํธ์ ์ํฅ์ ๋ฏธ์ณค์ต๋๋ค. ์๋ก์ด ํ๋ฉด ์ค์ ํ์์ ๋ ๊ฐ๊ฒฐํ๋ฉฐ, ๊ณ์ธต์ ์์ ์์คํ
์ ์ ๊ฑฐํ๊ณ ์ค์ ์ ๋ช
์์ ์ผ๋ก ์ง์ ํฉ๋๋ค.
์๋ฐ ์คํฌ๋ฆฝํธ
// eslint.config.js (ESLint v9 flat config)
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
export default [
js.configs.recommended,
...tseslint.configs.recommended,
{
languageOptions: {
globals: globals.browser,
},
rules: {
"no-unused-vars": "error",
"no-console": "warn",
"prefer-const": "error",
},
},
];
TypeScript์ฉ ESLint ํ์ํฉ๋๋ค typescript-eslint ์ด ํจํค์ง๋ ๊ธฐ์กด ํจํค์ง๋ฅผ ๋์ฒดํฉ๋๋ค. @typescript-eslint/eslint-plugin @typescript-eslint/parserTSC์์ ๊ฐ์ ํ์ง ์๋ 100๊ฐ ์ด์์ TypeScript ๊ด๋ จ ๊ท์น์ ์ ๊ณตํฉ๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
npm install --save-dev typescript-eslint
ESLint ๋ณด์ ํ๋ฌ๊ทธ์ธ ESLint์ ๋ณด์ ๊ด๋ จ ๊ท์น์ ์ถ๊ฐํ์ฌ ์ฌ์ฉ๊ณผ ๊ฐ์ ๋ฌธ์ ๋ฅผ ํ์งํฉ๋๋ค. eval()์์ ํ์ง ์์ ์ ๊ท ํํ์ ๋ฐ ํ๋กํ ํ์
์ฃผ์
:
์ธ๊ฒ ๋๋ฆฌ๋ค
npm install --save-dev eslint-plugin-security
์๋ฐ ์คํฌ๋ฆฝํธ
// eslint.config.js
import security from "eslint-plugin-security";
export default [security.configs.recommended];
ESLint๊ฐ ๋ค๋ฃจ๋ ๋ด์ฉ์ฝ๋ ์คํ์ผ, ํํ ๋ฐ์ํ๋ ๋ฒ๊ทธ (no-undef, no-unused-varsํ๋ฌ๊ทธ์ธ์ ํตํด ์ํฐ ํจํด, ํ๋ ์์ํฌ ๊ท์น ๋ฐ ๊ธฐ๋ณธ ๋ณด์ ํจํด์ ๊ตฌํํ ์ ์์ต๋๋ค.
ESLint๊ฐ ๋ค๋ฃจ์ง ์๋ ๋ด์ฉ : ํจ์ ํธ์ถ ์ ๋ฐ์ ๊ฑธ์น ๋ฐ์ดํฐ ํ๋ฆ/์ค์ผ ๋ถ์, ํ์ผ ๊ฐ ์ํฅ ๋ถ์, ์ข ์์ฑ ์ทจ์ฝ์ , ์ํคํ ์ฒ ๋งคํ ๋๋ ๋น๋๊ธฐ ๊ด๋ จ ์ทจ์ฝ์ ํจํด.
TypeScript: ์ปดํ์ผ๋ฌ ์์ค์์์ ์ ์ ์์ ์ฑ
TypeScript ์ปดํ์ผ๋ฌ(TSC)๋ JavaScript ํ๋ก์ ํธ์์ ์ฌ์ฉํ ์ ์๋ ๊ฐ์ฅ ๊ฐ๋ ฅํ ์ ์ ๋ถ์์ ์ํํฉ๋๋ค. ์ฆ, ๋ชจ๋ ํจ์ ๊ฒฝ๊ณ์์ ์ ์ฒด ์ฝ๋๋ฒ ์ด์ค์ ๊ฑธ์ณ ํ์
์ ํ์ฑ์ ๊ฒ์ฆํฉ๋๋ค. strict ๋ชจ๋ tsconfig.json ๊ฐ์ฅ ๋ง์ ๋ฌธ์ ๋ฅผ ํฌ์ฐฉํฉ๋๋ค:
JSON
{
"compilerOptions": {
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"exactOptionalPropertyTypes": true
}
}
noUnusedLocals noUnusedParameters ์ปดํ์ผ๋ฌ ์์ค์์ ์ฌ์ฉ๋์ง ์๋ ๋ณ์์ ํจ์ ๋งค๊ฐ๋ณ์๋ฅผ ํฌ์ฐฉํ๋ฉฐ, ESLint์ ๊ธฐ๋ฅ๊ณผ ์ผ๋ถ ๊ฒน์นฉ๋๋ค. no-unused-vars ํ์ง๋ง ํ์
์คํฌ๋ฆฝํธ ๊ด๋ จ ํจํด์ ๋ํด์๋ ๋ ์ ํํ๊ฒ ์ค๋ช
ํฉ๋๋ค.
ํ์
์คํฌ๋ฆฝํธ-eslint TypeScript์ ํ์
๊ฒ์ฌ๊ธฐ์ ESLint์ ๊ท์น ์์คํ
๊ฐ์ ๊ฐ๊ทน์ ๋ฉ์์ค๋๋ค. ๋ค์๊ณผ ๊ฐ์ ๊ท์น๋ค์ด ์์ต๋๋ค. @typescript-eslint/no-floating-promises @typescript-eslint/await-thenable TSC๋ ESLint๋ง์ผ๋ก๋ ์ก์๋ผ ์ ์๋ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ ์ค๋ฅ๋ฅผ ๊ฐ์งํ๊ธฐ ์ํด ํ์
์ ๋ณด๋ฅผ ํ์ฉํฉ๋๋ค.
์๋ฐ ์คํฌ๋ฆฝํธ
// eslint.config.js -- typescript-eslint with type-checked rules
import tseslint from "typescript-eslint";
export default tseslint.config(
...tseslint.configs.strictTypeChecked,
{
languageOptions: {
parserOptions: {
project: true, // enables type-aware rules
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/no-misused-promises": "error",
},
}
);
์ด ์ธ ๊ฐ์ง ๊ท์น์ ํนํ ์ด ๊ธฐ์ฌ์ ๊ฒ์ ์ฝ์ ๋ฐ์ดํฐ์ ๋ํ๋๋ async/await ์ค๋ฅ ํจํด์ ํด๊ฒฐํฉ๋๋ค. ์๋ชป๋ Promise ์ฒ๋ฆฌ๋ ์ต์ JavaScript์์ ๊ฐ์ฅ ํํ๊ฒ ๋ฐ์ํ๋ ๋ฒ๊ทธ ์ค ํ๋์
๋๋ค. typescript-eslint ๋ณ๋์ ๋๊ตฌ ์์ด๋ ํฌ์ฐฉํ ์ ์์ต๋๋ค.
Biome๊ณผ OxcLint: ์ฐจ์ธ๋ ์๋ฐ์คํฌ๋ฆฝํธ ํด
ESLint๋ 10๋ ๋์ ๊ธฐ๋ณธ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฆฐํฐ์์ต๋๋ค. ํ์ง๋ง ์ด์ ๋ ๊ฐ์ ์๋ก์ด ๋๊ตฌ๊ฐ ํจ์ฌ ๋ฐ์ด๋ ์ฑ๋ฅ์ผ๋ก ๊ทธ ์๋ฆฌ๋ฅผ ์ํํ๊ณ ์์ต๋๋ค.
Biome์ ESLint์ Prettier๋ฅผ ๋ชจ๋ ๋์ฒดํ๋ ๋จ์ผ ๋๊ตฌ๋ก, ๋ฆฐํ , ํฌ๋งทํ , ์ํฌํธ ๊ด๋ฆฌ ๊ธฐ๋ฅ์ ํ๋์ ๋ฐ์ด๋๋ฆฌ์ ์ ๊ณตํ๋ฉฐ ๊ธฐ๋ณธ ์ฌ์ฉ์๋ ๋ณ๋์ ์ค์ ์ด ํ์ํ์ง ์์ต๋๋ค. Rust๋ก ์์ฑ๋์์ผ๋ฉฐ ๋๊ท๋ชจ ์ฝ๋๋ฒ ์ด์ค์์ ESLint๋ณด๋ค 25~35๋ฐฐ ๋น ๋ฅธ ์๋๋ฅผ ์๋ํฉ๋๋ค. Biome์ JavaScript, TypeScript, JSX, JSON์ ์ง์ํฉ๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
# Install
npm install --save-dev --save-exact @biomejs/biome
# Initialize config
npx @biomejs/biome init
# Check (lint + format check)
npx @biomejs/biome check --write src/
OxcLint (Oxc ํ๋ก์ ํธ์ ์ผ๋ถ)๋ Rust ๊ธฐ๋ฐ์ ๋ ๋ค๋ฅธ ๋ฆฐํฐ๋ก, ESLint์ ํธํ๋๋ ๊ท์น์ 50~100๋ฐฐ ๋น ๋ฅธ ์๋๋ก ์คํํฉ๋๋ค. ESLint์ ํต์ฌ ๊ท์น์ ๋์ฒดํ๋๋ก ์ค๊ณ๋์์ผ๋ฉฐ, ๋ง์ด๊ทธ๋ ์ด์ ๊ณผ์ ์์ ESLint์ ํจ๊ป ์คํ๋๋๋ก ๊ณ ์๋์์ผ๋ฏ๋ก, ESLint๋ฅผ ์์ ํ ๊ต์ฒดํ ํ์๊ฐ ์์ต๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
# Install
npm install --save-dev oxlint
# Run
npx oxlint src/
๊ฐ ๋๊ตฌ๋ฅผ ์ธ์ ์ฌ์ฉํด์ผ ํ ๊น์ ? ์๋ก์ด ํ๋ก์ ํธ์ ๊ฒฝ์ฐ, Biome์ ๋ฆฐํ ๋ฐ ํฌ๋งทํ ์ ์ํ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๋จ์ผ ๋๊ตฌ์ ๋๋ค. ESLint ์ค์ ๊ณผ ํ๋ฌ๊ทธ์ธ์ด ๊ด๋ฒ์ํ๊ฒ ์ ์ฉ๋ ๊ธฐ์กด ํ๋ก์ ํธ์ ๊ฒฝ์ฐ, Biome์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ๋ ค๋ฉด ๊ท์น ์ ์ฉ ๋ฒ์๋ฅผ ๊ฒ์ฆํด์ผ ํฉ๋๋ค. OxcLint๋ ํ๋ฌ๊ทธ์ธ ์ํ๊ณ๋ฅผ ์ฆ์ ํฌ๊ธฐํ ์ ์๋ ๋๊ท๋ชจ ๊ธฐ์กด ํ๋ก์ ํธ์์ ESLint๋ฅผ ์ ์ง์ ์ผ๋ก ๋์ฒดํ๋ ๋ฐ ๋ ์ ํฉํฉ๋๋ค.
| ์๋จ | ์๋ vs ESLint | Prettier๋ฅผ ๋์ฒดํฉ๋๋ค. | ํ์ ์คํฌ๋ฆฝํธ ์ง์ | ํ๋ฌ๊ทธ์ธ ์ํ๊ณ |
|---|---|---|---|---|
| ESLint | ๊ธฐ์ค | ์๋์ (Prettier์ ์ง์ ์ด๋ฃจ์ธ์) | typescript-eslint๋ฅผ ํตํด | ๊ฐ์ฅ ํฐ ๊ท๋ชจ (~3,000๊ฐ ํ๋ฌ๊ทธ์ธ) |
| ๋ฐ์ด์ค ๋ฉ | 25~35๋ฐฐ ๋ ๋น ๋ฆ | ๊ฐ๋ฅ | ๋ด์ฅ | ์ ํ์ ์ด์ง๋ง ์ฑ์ฅ ์ค |
| ์ฅ์ฌ๋ฆฐํธ | 50~100๋ฐฐ ๋ ๋น ๋ฆ | ์๋ | ๋ด์ฅ | ESLint ํธํ ํ์ ์งํฉ |
| ์คํ ๋ค๋JS | ESLint์ ์ ์ฌํจ | ์ผ๋ถ์ | ์ ํ๋ | ๊ณ ์ ๊ท์น ์ธํธ |
Semgrep: ์๋ฐ์คํฌ๋ฆฝํธ ๋ณด์์ ์ํ ํจํด ๊ธฐ๋ฐ SAST
Semgrep์ ์ฝ๋ ํจํด ๋งค์นญ์ ํตํด ๋ณด์ ์ทจ์ฝ์ ์ ์ฐพ์๋ด๋ ๋ค๊ตญ์ด ์ ์ ๋ถ์ ๋ณด์ ํ ์คํธ(SAST) ๋๊ตฌ์ ๋๋ค. ESLint๊ฐ ์คํ์ผ๊ณผ ๊ท์น์ ๊ฐ์ ํ๋ ๋ฐ๋ฉด, Semgrep์ SQL ์ธ์ ์ , XSS, ํ๋กํ ํ์ ์ค์ผ, ํ๋์ฝ๋ฉ๋ ์๊ฒฉ ์ฆ๋ช , ์์ ํ์ง ์์ Express.js ๊ตฌ์ฑ ๋ฑ JavaScript ๋ฐ TypeScript ์ ๋ฐ์ ๊ฑธ์ณ ์๋ฐฑ ๊ฐ์ง์ ๋ณด์ ํจํด์ ์ฐพ์๋ ๋๋ค.
ESLint์์ ์ฃผ์ ์ฐจ์ด์ ์ Semgrep ๊ท์น์ด ๋์ ์ธ์ด์ ๊ตฌ๋ฌธ์ ๋งค์ฐ ์ ์ฌํ๊ฒ ๋ฐ์ํ๋ ์ฝ๋ ํจํด์ผ๋ก ์์ฑ๋์ด ์ ์ ๋ถ์์ ๋ํ ์ ๋ฌธ ์ง์์ด ์๋ ๊ฐ๋ฐ์๋ ์ฝ๊ณ ์์ฑํ ์ ์๋ค๋ ์ ์ ๋๋ค.
์
# Custom Semgrep rule: flag direct use of user input in SQL queries
rules:
- id: sql-injection-express
patterns:
- pattern: |
$APP.get($ROUTE, ($REQ, $RES) => {
...
$DB.query($REQ.query.$INPUT, ...);
...
})
message: User input directly used in SQL query -- use parameterized queries
languages: [javascript, typescript]
severity: ERROR
์ธ๊ฒ ๋๋ฆฌ๋ค
# Run Semgrep with the community security rule registry
semgrep scan --config=p/javascript src/
# Run with a specific rule set for Node.js
semgrep scan --config=p/nodejs src/
Semgrep๊ณผ ESLint ๋ ๊ฒฝ์ ๊ด๊ณ๊ฐ ์๋๋ผ ์ํธ ๋ณด์์ ์ธ ๊ด๊ณ์ ๋๋ค. ์ฝ๋ ํ์ง ๋ฐ ๊ท์น ์ค์์๋ ESLint๋ฅผ ์ฌ์ฉํ๊ณ , ๋ณด์ ๊ฒ์ฌ์๋ Semgrep์ ์ฌ์ฉํ์ธ์. ๋๋ถ๋ถ์ JavaScript ๊ฐ๋ฐํ์ CI ํ๊ฒฝ์์ ๋ ๋๊ตฌ ๋ชจ๋๋ฅผ ์คํํด์ผ ํฉ๋๋ค. GitLab์ ์ต๊ทผ SAST ๋ถ์ ๋๊ตฌ๋ฅผ ESLint์์ Semgrep์ผ๋ก ์ ํํ๋ค๊ณ ๋ฐํํ๋๋ฐ, ๋ณด์ ๊ฒ์ฌ ๋๊ตฌ๋ก์์ ESLint๋ ๋จ๊ณ์ ์ผ๋ก ํ์งํ๊ณ ๋ฆฐํ ๋๊ตฌ๋ก๋ ์ ์งํ ์์ ์ ๋๋ค. ์ด๋ ESLint๊ฐ ๋ฆฐํ ์ ์ ํฉํ๊ณ Semgrep์ด ๋ณด์ ๋ถ์์ ์ ํฉํ๋ค๋ ๊ณต๊ฐ๋๊ฐ ํ์ฑ๋๊ณ ์์์ ๋ฐ์ํฉ๋๋ค.
SonarQube์ SonarLint: ์ง์์ ์ธ ํ์ง ๊ด๋ฆฌ ์์คํ
SonarQube๋ ํ์ง ๊ฒ์ดํธ ๋ชจ๋ธ์ ์ ๊ณตํฉ๋๋ค. ๊ฐ ํ ๋ฆฌํ์คํธ๋ ์ ์๋ ํ์ง ํ๋กํ์ผ์ ๋ฐ๋ผ ํ๊ฐ๋๋ฉฐ, ์ฝ๋๊ฐ ๊ธฐ์ค์ ์ถฉ์กฑํ์ง ๋ชปํ๋ฉด ๋ณํฉ์ด ์ฐจ๋จ๋ฉ๋๋ค. JavaScript ๋ฐ TypeScript์ ๊ฒฝ์ฐ ๋ฒ๊ทธ, ์ฝ๋ ์ค๋ฉ, ๋ณด์ ์ทจ์ฝ์ ๋ฐ ์ค๋ณต์ ๊ฐ์งํ๊ณ ์๊ฐ ๊ฒฝ๊ณผ์ ๋ฐ๋ฅธ ์ถ์ธ๋ฅผ ์ถ์ ํฉ๋๋ค.
SonarLint ๋ ๊ฐ๋ฐ์๊ฐ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋์ SonarQube ๊ท์น์ ๋ก์ปฌ์ ํ์ํ์ฌ CI๋ฅผ ๊ธฐ๋ค๋ฆด ํ์ ์์ด ์ฆ๊ฐ์ ์ธ ํผ๋๋ฐฑ์ ๋ฐ์ ์ ์๋๋ก ํ๋ IDE ํ์ฅ ํ๋ก๊ทธ๋จ์ ๋๋ค.
SonarQube๊ฐ ์์ ๋ฆฐํ ๋๊ตฌ์ ์ฐจ๋ณํ๋๋ ์ ์ ์ง์์ ์ธ ์ธก์ ๋ชจ๋ธ์ ๋๋ค. SonarQube๋ ๊ธฐ์ ๋ถ์ฑ, ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง, ๋ณด์ ์ทจ์ฝ์ ์ ๋ณํ๋ฅผ ์๊ฐ ๊ฒฝ๊ณผ์ ๋ฐ๋ผ ์ถ์ ํฉ๋๋ค. ๊ฐ๋ฐ์์ฉ ์ง๋จ ๊ธฐ๋ฅ๊ณผ ๋๋ถ์ด ๊ด๋ฆฌ์ ์์ค์ ์ฝ๋ ํ์ง ๋ณด๊ณ ์๊ฐ ํ์ํ ํ์๊ฒ SonarQube๋ ์ต์ ์ ์ ํ์ ๋๋ค.
JavaScript/TypeScript ํ๋ก์ ํธ์ ์ฃผ์ ์ค์ :
- ์๋ก์ด ์ฐจ๋จ ์์ ๋๋ ์ค์ํ ๋ณด์ ์ทจ์ฝ์ ์ด ๋ฐ์ํ ๊ฒฝ์ฐ ์คํจํ๋ ํ์ง ๊ฒ์ดํธ๋ฅผ ์ค์ ํ์ญ์์ค.
- ํ์ฑํ
Sonar Way๊ท์น ํ๋กํ์ ๊ธฐ์ค์ ์ผ๋ก ์ฌ์ฉ - VS Code ๋๋ IntelliJ์์ SonarLint์ ํจ๊ป ์ฌ์ฉํ๋ฉด ์๋ํฐ ๋ด์์ ๋ฐ๋ก ํผ๋๋ฐฑ์ ๋ฐ์ ์ ์์ต๋๋ค.
- GitHub Actions ๋๋ GitLab CI์ ํตํฉํ๋ ค๋ฉด ๋ค์์ ์ฌ์ฉํ์ธ์.
SonarQube Scan๋์
CodeQL: ์ฌ์ธต์ ์ธ ์ทจ์ฝ์ ํ์ง๋ฅผ ์ํ ์๋งจํฑ ์ฝ๋ ์ค์บ๋
GitHub์์ ๊ฐ๋ฐํ CodeQL์ ์ฝ๋๋ฅผ ์ฟผ๋ฆฌ ๊ฐ๋ฅํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ๋ณํํ๊ณ ํด๋น ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ํด ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ฌ ์๋ฏธ ๋ถ์์ ์ํํฉ๋๋ค. JavaScript์ TypeScript๋ฅผ ์ง์ํ๋ฉฐ GitHub Advanced Security๋ฅผ โโํตํด ์คํ ์์ค ํ๋ก์ ํธ์์ ๋ฌด๋ฃ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
CodeQL์ ํ๋ก๊ทธ๋จ ์ ์ฒด์์ ๋ฐ์ดํฐ๊ฐ ์ด๋ป๊ฒ ํ๋ฅด๋์ง ์ดํดํด์ผ ํ๋ ์ทจ์ฝ์ ์ ์ฐพ์๋ ๋๋ค. ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ์ ์ดํ๋ โโ๊ฐ์ด ์ฌ๋ฌ ํจ์ ํธ์ถ์ ๊ฑฐ์ณ ์์ ํ์ง ์์ ์์ ์ ๋๋ฌํ๋ ๊ฒฝ์ฐ๋ฅผ ๋ถ์ํฉ๋๋ค. CodeQL์ ์ฝ๋ ๊ฒฝ๋ก๊ฐ ๊ฐ์ ์ ์ธ ๊ฒฝ์ฐ Semgrep๊ณผ ๊ฐ์ ํจํด ๋งค์นญ ๋๊ตฌ๊ฐ ๋์น๋ ์ทจ์ฝ์ ์ ์ก์๋ด๋ ๋ฐ ํจ๊ณผ์ ์ ๋๋ค.
์
# .github/workflows/codeql.yml
name: CodeQL Analysis
on: [push, pull_request]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: javascript-typescript
- uses: github/codeql-action/autobuild@v3
- uses: github/codeql-action/analyze@v3
CodeQL์ Semgrep๋ณด๋ค ์ค์ ๋น์ฉ์ด ๋ ๋ง์ด ๋ค๊ณ ์คํ ์๋๋ ๋๋ฆฌ์ง๋ง, ํจํด ๊ธฐ๋ฐ ๋๊ตฌ๋ก๋ ์ ์ฒด ๋ฐ์ดํฐ ํ๋ฆ ๋ถ์ ์์ด๋ ์๋ณํ ์ ์๋ ํจ์ ๊ฐ, ํ์ผ ๊ฐ ์ค์ผ ํ๋ฆ๊ณผ ๊ฐ์ ๋ค๋ฅธ ์ ํ์ ์ทจ์ฝ์ ์ ํฌ์ฐฉํฉ๋๋ค.
์ฌ์ฉ๋์ง ์๋ ์ฝ๋ ๊ฐ์ง: ์ฌ์ฉ๋์ง ์๋ ๋ด๋ณด๋ด๊ธฐ ๋ฐ ์ ๊ทผํ ์ ์๋ ์ฝ๋
์๋ฐ์คํฌ๋ฆฝํธ์ ํ์ ์คํฌ๋ฆฝํธ ํ๋ก์ ํธ์์ ์ฌ์ฉ๋์ง ์๋ ์ฝ๋๋ ํนํ ๋ฌธ์ ๊ฐ ๋๋๋ฐ, ๋ชจ๋ ์์คํ ์ด ์ฌ์ฉ๋์ง ์๋ ๋ด๋ณด๋ด๊ธฐ๊ฐ ๋์ ๋๋ ๊ฒ์ ๋ง์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ํจ์๊ฐ ๋ด๋ณด๋ด์ก์ง๋ง ํ ๋ฒ๋ ๊ฐ์ ธ์ค์ง ์์ ๊ฒฝ์ฐ, ํน๋ณํ ์ค์ ํ์ง ์์ผ๋ฉด ์ด๋ค ํ์ค ๋๊ตฌ๋ ์ด์ ๋ํด ๊ฒฝ๊ณ ํ์ง ์์ต๋๋ค.
์๋ฅด๋ค ํ์ฌ๋ก์๋ ์ด๋ฅผ ์ํ ๊ฐ์ฅ ๋ฐ์ด๋ ๋๊ตฌ์
๋๋ค. ์ ์ฒด ํ๋ก์ ํธ ๊ทธ๋ํ๋ฅผ ๋ถ์ํ์ฌ ์ฌ์ฉ๋์ง ์๋ ๋ด๋ณด๋ด๊ธฐ, ์ฌ์ฉ๋์ง ์๋ ์ข
์์ฑ ๋ฑ์ ์ฐพ์๋
๋๋ค. package.json๊ทธ๋ฆฌ๊ณ ์ ๊ทผํ ์ ์๋ ํ์ผ:
์ธ๊ฒ ๋๋ฆฌ๋ค
npm install --save-dev knip
npx knip
ts-prune์ ํนํ TypeScript๋ฅผ ๋์์ผ๋ก ํ๋ฉฐ, ๊ฐ์ ธ์ค์ง ์๋ ๋ด๋ณด๋ธ ์ฌ๋ณผ์ ์ฐพ์๋ ๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
npm install --save-dev ts-prune
npx ts-prune
ESLint no-unused-vars @typescript-eslint/no-unused-vars ESLint๋ ํ์ผ ๋ด์์ ์ฌ์ฉ๋์ง ์๋ ์ง์ญ ๋ณ์๋ฅผ ์ฐพ์๋ผ ์ ์์ง๋ง, ๋ชจ๋ ์์ค์์ ์ฌ์ฉ๋์ง ์๋ ๋ด๋ณด๋ด๊ธฐ๋ ๊ฐ์งํ ์ ์์ต๋๋ค. Knip์ ESLint๊ฐ ๋์น๋ ๋ถ๋ถ์ ๋ณด์ํฉ๋๋ค.
์ฌ์ฉ๋์ง ์๋ ์ฝ๋(๋ฐ๋ ์ฝ๋)๋ ํ๋ฐํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฒ๋ค ํฌ๊ธฐ์ ์ง์ ์ ์ธ ์ํฅ์ ๋ฏธ์น๊ณ , ์ฝ๋๋ฒ ์ด์ค๋ฅผ ๋ค๋ฃจ๋ ๊ฐ๋ฐ์์ ์ธ์ง ๋ถํ๋ฅผ ์ฆ๊ฐ์ํต๋๋ค. ๋ฐ๋ ์ฝ๋ ์ ๊ฑฐ๋ ๊ฐ์ฅ ํจ๊ณผ์ ์ธ ์ ์ง๋ณด์ ํ๋ ์ค ํ๋์ด๋ฉฐ, ๋๊ท๋ชจ ์ฝ๋๋ฒ ์ด์ค์์ ๋ชจ๋ ์์ค์ ์ฌ์ฉ ๋น๋๋ฅผ ์ฌ๋์ด ์ง์ ์ถ์ ํ๋ ๊ฒ์ ๋ถ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ๋๊ตฌ๋ฅผ ํตํด์๋ง ๋ฐ๊ฒฌํ ์ ์์ต๋๋ค.
Async/await ๋ฐ Promise: ์ ์ ๋ถ์ ๊ณผ์
์ด ๊ธฐ์ฌ์ ๋ํ ๊ฒ์ ์ฝ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ฉด TAJS, Jelly ์ ์ ๋ถ์๊ธฐ, SonarJS ๋น๋๊ธฐ ๊ท์น ๋ฑ๊ณผ ๊ฐ์ ๋น๋๊ธฐ JavaScript์ฉ ์ ์ ๋ถ์ ๋๊ตฌ์ ๋ํ ๊ฒ์์ด๊ฐ ์๋น์ ์ง์ค๋์ด ์๋ ๊ฒ์ ์ ์ ์์ต๋๋ค. ์ด๋ ๋๊ตฌ ์์ฅ์ ์ค์ ๋ก ๋ถ์กฑํ ๋ถ๋ถ์ด ์์์ ๋ฐ์ํฉ๋๋ค.
ํ์ค ๋ฆฐํ
๋๊ตฌ๋ Promise์ ๋น๋๊ธฐ ํจ์๊ฐ ์ํธ ์์ฉํ๋ ๋ฐฉ์์ ๋ชจ๋ธ๋งํ์ง ์์ต๋๋ค. ๋๋ฝ๋ ๋ถ๋ถ์ด ์์ต๋๋ค. await์ฒ๋ฆฌ๋์ง ์์ ๊ฑฐ๋ถ ๋๋ ๋์ ๋น๋๊ธฐ ์ฝ๋์ ๊ฒฝ์ ์กฐ๊ฑด์ ๊ตฌ๋ฌธ์ ์ผ๋ก๋ ์ ํจํด ๋ณด์ด๊ณ ๋ชจ๋ ๋ฆฐํธ ๊ท์น์ ํต๊ณผํ๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ๊ฐ์งํ๋ ค๋ฉด ๋น๋๊ธฐ ์คํ ์๋ฏธ๋ก ์ ๋ชจ๋ธ๋งํ๋ ๋๊ตฌ๊ฐ ํ์ํฉ๋๋ค.
ํ์ฌ์ ์ค์ง์ ์ธ ์ ๊ทผ ๋ฐฉ์ :
typescript-eslint ๋น๋๊ธฐ ๊ด๋ จ ๊ท์น ์ค ๊ฐ์ฅ ์ฆ์ ํ์ฉ ๊ฐ๋ฅํ ๊ท์น์ ์ ๊ณตํฉ๋๋ค.
์๋ฐ ์คํฌ๋ฆฝํธ
// Rules that catch common async mistakes
"@typescript-eslint/no-floating-promises": "error", // await or .catch() required
"@typescript-eslint/await-thenable": "error", // only await actual Promises
"@typescript-eslint/no-misused-promises": "error", // Promises in non-async contexts
"@typescript-eslint/require-await": "warn", // async functions must use await
TAJS(Type Analyzer for JavaScript), Jelly, SAFE์ ๊ฐ์ ์ฐ๊ตฌ ๋๊ตฌ๋ Promise ์ฒด์ธ, async/await, ์ด๋ฒคํธ ๋ฃจํ ์๋ฏธ๋ก ์ ํฌํจํ JavaScript์ ๋น๋๊ธฐ ์คํ ๋ชจ๋ธ์ ๋ชจ๋ธ๋งํ๋ ํ์ ์ ์ธ ์ ์ ๋ถ์๊ธฐ์ ๋๋ค. ์ด๋ฌํ ๋๊ตฌ๋ ์ค์ ๊ฐ๋ฐ ํ๊ฒฝ์์ ์ฌ์ฉ๋๋ ๋๊ตฌ๊ฐ ์๋๋ผ ์ทจ์ฝ์ ์ฐ๊ตฌ ๋ฐ ํ์ ๋ถ์ ์์ ์ ์ฌ์ฉ๋๋ ์ฐ๊ตฌ ํ๋ซํผ์ ๋๋ค. ๊ฒ์ ์ฝ์ ๋ฐ์ดํฐ์์ "jelly static analyzer javascript async support paper" ๋ฐ "TAJS async await support"์ ๊ฐ์ ๊ฒ์์ด๋ ๊ฐ๋ฐ์๋ค์ด ์ผ์์ ์ธ ๊ฐ๋ฐ ๋๊ตฌ๋ฅผ ์ฐพ๋ ๊ฒ์ด ์๋๋ผ ์ด๋ฌํ ํ์ ์ ์ธ ๋๊ตฌ๋ฅผ ์ฐ๊ตฌํ๊ฑฐ๋ ์ธ์ฉํ๋ ๊ฒ์ ๋ฐ์ํฉ๋๋ค.
SonarQube์ javascript:S4328 ๊ด๋ จ ๋น๋๊ธฐ ๊ท์น์ ํ๋ก๋์
ํ์ง ๋ถ์์์ ํํ ๋ฐ์ํ๋ ๋ช ๊ฐ์ง ๋น๋๊ธฐ ์ํฐ ํจํด์ ๊ฐ์งํฉ๋๋ค.
์ค์ ํ๋ก๋์
ํ๊ฒฝ์์ ์ฌ์ฉํ๋ ค๋ฉด TypeScript์ ํ์
๊ฒ์ฌ๊ธฐ์ ๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. typescript-eslintSonarQube์ ๋น๋๊ธฐ ์ธ์ ๊ท์น๊ณผ ํ์ง ๊ฒ์ดํธ๋ ์ค๋๋ ํ์ค ๋๊ตฌ์์ ์ฌ์ฉํ ์ ์๋ ๊ฐ์ฅ ์ฒ ์ ํ ๋น๋๊ธฐ ์์ ์ฑ์ ์ ๊ณตํฉ๋๋ค.
Snyk Code: ๊ฐ๋ฐ์ ์ฐ์ ๋ณด์ ์ค์บ
Snyk Code๋ ๊ฐ๋ฐ์ ๊ฒฝํ์ ์ค์ ์ ๋ SAST ์ค์บ๋ ์๋ฃจ์ ์ ์ ๊ณตํฉ๋๋ค. VS Code ๋ฐ JetBrains IDE์ ํตํฉ๋์ด ๊ฐ๋ฐ์๊ฐ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋์ ๋ฐ๊ฒฌ ์ฌํญ์ ๋ฐ๋ก ํ์ํ๊ณ , ๊ฐ ๋ฐ๊ฒฌ ์ฌํญ์ ๋ํ ํด๊ฒฐ ์์๋ฅผ ์ ๊ณตํฉ๋๋ค. ์์ฒด ๊ฐ๋ฐํ ๋จธ์ ๋ฌ๋ ๊ธฐ๋ฐ ๋ถ์ ์์ง์ ์ฌ์ฉํ์ฌ JavaScript ๋ฐ TypeScript ์ฝ๋๋ฒ ์ด์ค ์ ๋ฐ์ ๊ฑธ์ณ ์ค์ผ ์ถ์ ์ ์ํํฉ๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
# Install Snyk CLI
npm install --save-dev snyk
# Authenticate and scan
npx snyk auth
npx snyk code test
Snyk Code๋ IDE๋ฅผ ๋ฒ์ด๋์ง ์๊ณ ๋ณด์ ํผ๋๋ฐฑ์ ๋ฐ๊ณ ์ ํ๋ ํ์๊ฒ ํนํ ํจ๊ณผ์ ์ ๋๋ค. Snyk Code์ ์์ ์ ์์ CodeQL์ ์ฟผ๋ฆฌ ์ค์ฌ ์ถ๋ ฅ๋ณด๋ค ๊ฐ๋ฐ์ ์นํ์ ์ด๋ฏ๋ก ์ทจ์ฝ์ ํ์ง์ ํจ๊ป ๋ณด์ ๊ต์ก์ ๋ ์ ํฉํฉ๋๋ค.
๊ณ์ธตํ JavaScript ์ ์ ๋ถ์ ์คํ ๊ตฌ์ถ
์๋ฐ์คํฌ๋ฆฝํธ ์ ์ ๋ถ์์ ๋ํ ์ฌ๋ฐ๋ฅธ ์ ๊ทผ ๋ฐฉ์์ ํ๋์ ๋๊ตฌ๋ฅผ ์ ํํ๋ ๊ฒ์ด ์๋๋ผ, ์ค๋ณต์ด ํฌ์ง ์์ผ๋ฉด์ ์๋ก ๋ค๋ฅธ ๊ณ์ธต์ ๋ค๋ฃจ๋ ๋๊ตฌ๋ค์ ์กฐํฉํ๋ ๊ฒ์ ๋๋ค.
| ์ธต | ์๋จ | ๊ทธ๊ฒ์ด ์คํ๋ ๋ |
|---|---|---|
| ์์ | ์ํ๊ณ ๋๋ ๋ ์๋ฆ๋ค์ด | ์ฌ์ ์ปค๋ฐ(๋น ๋ฅธ) |
| ๋ณดํ | ESLint + typescript-eslint | ์ฌ์ ์ปค๋ฐ + CI |
| ์ ํ ํ์ธ | tsc --noEmit | CI |
| ๋ณด์ ์ค์บ๋ | Semgrep ๋๋ Snyk ์ฝ๋ | CI (๋ชจ๋ PR) |
| ์ฌ์ธต ์ทจ์ฝ์ ์ค์บ๋ | CodeQL | CI (์์ ๋๋ PR) |
| ์ฃฝ์ ์ฝ๋ ๊ฐ์ง | ์๋ฅด๋ค | CI (์ฃผ๊ฐ ๋๋ ์๊ฐ) |
| ํ์ง ๊ด๋ฆฌ ๊ฒ์ดํธ + ์ถ์ธ ์ถ์ | ์๋ํ๋ธ | CI (๋ชจ๋ PR) |
| ์ข ์์ฑ ์ทจ์ฝ์ ์ค์บ | npm audit + ์ค๋ | CI(๋ชจ๋ ๋น๋) |
ํ์ด ์๋ฌด๊ฒ๋ ์๋ ์ํ์์ ์์ํ ๋ ํ์ํ ์ต์ํ์ ์คํ: ESLint + typescript-eslint + npm audit๋ณด์ ์๊ตฌ ์ฌํญ์ด ์ฆ๊ฐํ๋ฉด Semgrep ๋๋ Snyk Code๋ฅผ ์ถ๊ฐํ์ญ์์ค. ํ์์ ํ์ง ์ถ์ธ ๊ฐ์์ฑ ๋ฐ ๊ด๋ฆฌ ๋ณด๊ณ ๊ฐ ํ์ํ๋ฉด SonarQube๋ฅผ ์ถ๊ฐํ์ญ์์ค.
์
# .github/workflows/quality.yml
name: JavaScript Code Quality
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: "20" }
- run: npm ci
- run: npx tsc --noEmit
- run: npx eslint src/ --max-warnings 0
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: "20" }
- run: npm ci
- run: npm audit --audit-level=high
- run: npx semgrep scan --config=p/javascript --error src/
์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ๋๊ท๋ชจ ์ํฐํ๋ผ์ด์ฆ ์์คํ ์์ ์ฌ์ฉ๋ ๋
๊ธฐ์ ํ๊ฒฝ์์ JavaScript ๋ฐ TypeScript ์๋น์ค๋ COBOL ํ๋ก๊ทธ๋จ, Java ๋ฐฑ์๋, Python ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ, ๊ทธ๋ฆฌ๊ณ ๊ธฐ์กด ๋ฉ์ธํ๋ ์ ์์คํ ๊ณผ ์ ์ ๋ ๊ณต์กดํ๊ณ ์์ต๋๋ค. ์ด๋ฌํ ๋งฅ๋ฝ์์ ์์ ์ ์ ๋ถ์ ๋๊ตฌ๋ค์ JavaScript ๊ฒฝ๊ณ ๋ด์์๋ ์ฒ ์ ํ ๊ฐ์์ฑ์ ์ ๊ณตํ์ง๋ง, ๊ทธ ๊ฒฝ๊ณ๋ฅผ ๋์ด์๋ ์ฐ๊ฒฐ์ ๋ํด์๋ ์ ํ ํ์ ํ์ง ๋ชปํฉ๋๋ค.
COBOL ๋ฐฐ์น ์์ ์ผ๋ก ์ฑ์์ง ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๋ Node.js ์๋น์ค๋ ์ด๋ค JavaScript ๋ถ์ ๋๊ตฌ๋ ํ์ ํ ์ ์๋ ๋ฐฉ์์ผ๋ก ํด๋น COBOL ํ๋ก๊ทธ๋จ์ ์์กดํฉ๋๋ค. COBOL ํ๋ก๊ทธ๋จ์ ํธ์ถํ๋ Java API๋ฅผ ํธ์ถํ๋ React ํ๋ฐํธ์๋๋ ์ธ ๊ฐ์ง ์ธ์ด ๊ฒฝ๊ณ๋ฅผ ๋๋๋๋ ์์กด์ฑ ์ฒด์ธ์ ๊ฐ์ง๊ณ ์๋๋ฐ, ์ด ์ฒด์ธ์ ์ด๋ค ๋จ์ผ ์ธ์ด ๋ถ์ ๋๊ตฌ์ ๊ด์ ์์๋ ๋ณด์ด์ง ์์ต๋๋ค.
SMART TS XL ์ด ์๋ฃจ์ ์ ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ํฌํธํด๋ฆฌ์ค์ ๊ฑธ์ณ ์ธ์ด ๊ฐ ์์กด์ฑ ๋ถ์์ ์ ๊ณตํจ์ผ๋ก์จ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํฉ๋๋ค. JavaScript ๋ชจ๋์ด ๊ณต์ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ์ด๋ป๊ฒ ์์กดํ๋์ง, API ๊ณ์ฝ์ด ํ๋ฐํธ์๋์ ๋ฐฑ์๋ ์๋น์ค๋ฅผ ์ด๋ป๊ฒ ์ฐ๊ฒฐํ๋์ง, ๊ทธ๋ฆฌ๊ณ ์์คํ ์ ํ ๋ถ๋ถ์์ ๋ฐ์ํ ๋ณ๊ฒฝ ์ฌํญ์ด ๋ค๋ฅธ ์ธ์ด์ ๊ตฌ์ฑ ์์์ ์ด๋ป๊ฒ ์ ํ๋๋์ง๋ฅผ ๋ํ๋ด๋ ํตํฉ ๋ชจ๋ธ์ ๊ตฌ์ถํฉ๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก ๊ทธ ์๋ฃจ์ ์ ๋๋ค. ๋ค๊ตญ์ด ์ํคํ ์ฒ ๋ถ์ ์ด๋ ์ํฐํ๋ผ์ด์ฆ ์ํคํ ์ฒ ํ์ด ์ฌ๋ฌ ์ธ์ด์ ํ๋ซํผ์ ๊ฑธ์ณ ์๋ ์์คํ ๋ณ๊ฒฝ์ ๊ณํํ ๋ ํ์ํ ๊ธฐ๋ฅ์ด๋ฉฐ, ์ด ๊ฐ์ด๋์์ ๋ค๋ฃจ๋ ์๋ฐ์คํฌ๋ฆฝํธ ๊ด๋ จ ๋๊ตฌ์ ๊ฒฝ์ํ๋ ๊ฒ์ด ์๋๋ผ ๋ณด์ํ๋ ๊ธฐ๋ฅ์ ๋๋ค. ์ด๋ฌํ ๋งฅ๋ฝ์์ ์ค๋ช ํ์๋ฉด, ์์กด์ฑ ๊ทธ๋ํ ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ํ๋ณ๊ฒฝ์ ํ๊ธฐ ์ ์ ์์คํ ์ ์ ์ฒด ์์กด์ฑ ๊ตฌ์กฐ๋ฅผ ์ดํดํ๋ ๊ฒ์ด ์์ ํ ๋ฆฌํฉํ ๋ง๊ณผ ์๋ฌด๋ ํ ์คํธํ ์๊ฐ์ ํ์ง ๋ชปํ ๊ตฌ์ฑ ์์์์ ์์์น ๋ชปํ ์ค๋ฅ๋ฅผ ๋ฐ์์ํค๋ ๋ณ๊ฒฝ์ ๊ตฌ๋ถํ๋ ๊ธฐ์ค์ ๋๋ค.
์ด๋ฌํ ๋๊ท๋ชจ ํ๊ฒฝ ๋ด์์ JavaScript์ ํนํ๋ ๋ถ์์ ์ํํ๋ ค๋ฉด, SMART TS XL์ ์ํฐํ๋ผ์ด์ฆ ์ฝ๋ ์ธํ ๋ฆฌ์ ์ค ์ด ์๋ฃจ์ ์ COBOL, JCL, Java, Python ๋ฐ ๊ธฐํ ์ํฐํ๋ผ์ด์ฆ ์ธ์ด๋ ๋ฌผ๋ก JavaScript ๋ฐ TypeScript๋ฅผ ์ง์ํ๋ฉฐ, ๋จ์ผ ํ๋ซํผ์์ ํตํฉ๋ ํ์ง ์งํ์ ์ข ์์ฑ ๊ฐ์์ฑ์ ์ ๊ณตํฉ๋๋ค.
์ํฉ์ ๋ง๋ ์ ์ ํ ๋๊ตฌ ์ ํํ๊ธฐ
๋จ์ผ ๋๊ตฌ๋ก ์๋ฐ์คํฌ๋ฆฝํธ ์ ์ ๋ถ์์ ๋ชจ๋ ์ธก๋ฉด์ ๋ค๋ฃฐ ์๋ ์์ต๋๋ค. ์ด๋ค ๋๊ตฌ๋ฅผ ์ ํํ ์ง๋ ํ ๊ท๋ชจ, ๋ณด์ ์๊ตฌ ์ฌํญ, ๊ธฐ์กด ๋๊ตฌ ๋ชจ์, ๊ทธ๋ฆฌ๊ณ ์๋ฐ์คํฌ๋ฆฝํธ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ ๋ฆฝ์ ์ผ๋ก ์๋ํ๋์ง ์๋๋ฉด ๋ ํฐ ๋ค๊ตญ์ด ์ํฐํ๋ผ์ด์ฆ ์์คํ ์ ์ผ๋ถ๋ก ์๋ํ๋์ง์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ค.
์๋ก์ด ํ๋ก์ ํธ๋ฅผ ์งํํ๋ 1์ธ ๊ฐ๋ฐ์ ๋๋ ์๊ท๋ชจ ํ์ ๊ฒฝ์ฐ: Biome(๋ฆฐํ
๋ฐ ํฌ๋งทํ
)๊ณผ TypeScript ์๊ฒฉ ๋ชจ๋๋ถํฐ ์์ํ์ธ์. npm audit ์ข
์์ฑ ๋ณด์์ ์ํด์์
๋๋ค.
์ค๊ท๋ชจ ํ์ด ์ค์ ์ด์ ํ๊ฒฝ์ ๋ง๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ๊ฒฝ์ฐ, ESLint(typescript-eslint ํฌํจ), Prettier, TypeScript ์๊ฒฉ ๋ชจ๋, CI ํ๊ฒฝ์์์ ๋ณด์ ๊ฐํ๋ฅผ ์ํ Semgrep, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ๋์ง ์๋ ์ฝ๋ ํ์ง๋ฅผ ์ํ Knip์ ์ถ์ฒํฉ๋๋ค.
๊ท์ ์ค์ ๋ฐ ๋ณด์ ์๊ตฌ ์ฌํญ์ด ์๋ ์ํฐํ๋ผ์ด์ฆ ํ์ ๊ฒฝ์ฐ: ํ์ง ๊ฒ์ดํธ ๋ฐ ์ถ์ธ ์ถ์ ์ ์ํ SonarQube, ์ฌ์ธต ์ทจ์ฝ์ ์ค์บ์ ์ํ CodeQL, ๊ฐ๋ฐ์ ๋์ ๋ณด์ ํผ๋๋ฐฑ์ ์ํ Snyk Code, SMART TS XL ์๋ฐ์คํฌ๋ฆฝํธ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ ๊ฑฐ์ ์์คํ ์ด๋ ๋ค๊ตญ์ด ์์คํ ๊ณผ ์ํธ ์์ฉํ๋ ๊ฒฝ์ฐ.
๋ชจ๋ ธ๋ ํฌ ํ๊ฒฝ์์ ์ฑ๋ฅ ๋๋ฌธ์ ESLint ๋์์ ํ๊ฐํ๋ ํ์ด๋ผ๋ฉด, ์๋์ ์ค์ ์ ๋ OxcLint ๋๋ ๋ฆฐํฐ์ ํฌ๋งทํฐ๋ฅผ ์๋ฒฝํ๊ฒ ๋์ฒดํ ์ ์๋ Biome์ ๊ณ ๋ คํด ๋ณผ ์ ์์ต๋๋ค.


