Last Updated on 1์›” 23rd, 2024, By
 In AppSealing News, ์•ฑ์‹ค๋ง ๋ธ”๋กœ๊ทธ

2013๋…„ ํŽ˜์ด์Šค๋ถ ํ•ด์ปคํ†ค์—์„œ ์•„์ด๋””์–ด๊ฐ€ ๋‚˜์™”๊ณ , 2015๋…„์— ๋Œ€์ค‘์„ ์œ„ํ•œ ์˜คํ”ˆ ์†Œ์Šค ํ”Œ๋žซํผ์œผ๋กœ ์ถœ์‹œ๋œ ๊ฒƒ์ด ์ด์ œ๋Š” ์•ฑ ๊ฐœ๋ฐœ์— ์žˆ์–ด ๊ฐ€์žฅ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. React Native๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœํ•˜๋ฉฐ ๊ฐœ๋ฐœ์ž๊ฐ€ iOS์™€ Android ์šด์˜ ์ฒด์ œ ๋ชจ๋‘์— ๋Œ€ํ•ด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ Œ๋”๋ง๋œ ๋ชจ๋ฐ”์ผ ์•ฑ์„ ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์•ฑ๊ณผ ์›น ์‚ฌ์ดํŠธ์šฉ UI ์ธํ„ฐํŽ˜์ด์Šค์™€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋นŒ๋“œํ•˜๊ธฐ ์œ„ํ•œ ์˜คํ”ˆ ์†Œ์Šค ํ”„๋ก ํŠธ์—”๋“œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ React JavaScript(ReactJS)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ณธ ๊ธ€์—์„œ๋Š” ์˜คํ”ˆ ์†Œ์Šค ๋ชจ๋ฐ”์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ๊ตฌ์ถ•๋œ ์•ฑ์— ๋Œ€ํ•œ React Native ๋ณด์•ˆ์— ๋Œ€ํ•ด์„œ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.ย 

React Native ๋ณด์•ˆ

๋ชจ๋“  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋ฐ˜ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ React Native๋Š” ๋ณด์•ˆ ์œ„ํ˜‘์— ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค. React Native ๋ณดํ˜ธ ๊ด€์ ์—์„œ์˜ ๋ถ„์„์€ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„๊ณผ ์ด๋“ค ๊ฐ„์˜ ์—ฐ๊ฒฐ์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์†Œ์Šค์ฝ”๋“œ๋Š” ํด๋ผ์ด์–ธํŠธ๋‹จ์—์„œ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์–ผ๋งˆ๋“ ์ง€ ๋ณผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋ณธ์งˆ์ ์œผ๋กœ ํ”„๋ŸฐํŠธ์—”๋“œ JS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ˆ˜์ •๋˜๊ฑฐ๋‚˜ ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์‰ฝ๊ฒŒ ์œ ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. JS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๋ณด์•ˆ ๋ฌธ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

ํฌ๋กœ์Šค์‚ฌ์ดํŠธ ์Šคํฌ๋ฆฝํŒ…(XSS; Cross-site Scripting)

XSS ๊ณต๊ฒฉ์ด๋ผ๊ณ ๋„ ํ•˜๋ฉฐ ๊ณต๊ฒฉ์ž๊ฐ€ ์›น ์‚ฌ์ดํŠธ๋ฅผ ์†์—ฌ ์‚ฌ์šฉ์ž์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ž„์˜์˜ JS ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. XSS ๊ณต๊ฒฉ์—๋Š” ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. Reflected XSS๋Š” ์ผ๋ถ€ ํ…์ŠคํŠธ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ ๋งํฌ๊ฐ€ ๋ธŒ๋ผ์šฐ์ € ์ƒ์—์„œ ์ฝ”๋“œ๋กœ ์ฒ˜๋ฆฌ๋  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. Stored XSS๋Š” ๊ณต๊ฒฉ์ž๊ฐ€ ์„œ๋ฒ„ ์•ก์„ธ์Šค ๊ถŒํ•œ์„ ์–ป๊ณ  ํด๋ผ์ด์–ธํŠธ์˜ ์›น ํŽ˜์ด์ง€ ์ •๋ณด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๊ฐ€ ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.ย 

๋ถˆ์•ˆ์ „ํ•œ ๋ฌด์ž‘์œ„์„ฑ๊ณผ ๋งํฌ

์ด๊ฒƒ์€ ๋งํฌ๊ฐ€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ž…๋ ฅํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ , ๊ณต๊ฒฉ์ž๊ฐ€ ์›๋ž˜ JS ์ฝ”๋“œ์— ์•…์„ฑ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ํด๋ฆญํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์—์„œ ๊ณต๊ฒฉ์ž์˜ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„์ธก ๋ Œ๋”๋ง ๊ณต๊ฒฉ์ž์— ์˜ํ•ด์„œ ์ œ์–ด๋˜๋Š” โ€‹โ€‹์ดˆ๊ธฐ ์ƒํƒœ

์ด๊ฒƒ์€ ์•ฑ์ด ์„œ๋ฒ„์ธก์—์„œ ๋ Œ๋”๋ง๋˜๋Š” ๊ฒฝ์šฐ์— ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€์˜ ๊ธฐ๋ณธ ๋ฒ„์ „์„ ์ƒ์„ฑํ•˜๋ฉด JSON ๋ฌธ์ž์—ด์—์„œ ๋ฌธ์„œ ๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. JSON.stringify () ํ•จ์ˆ˜์— ์ œ๊ณต๋œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ํŽ˜์ด์ง€์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„ํ—˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž„์˜ ์ฝ”๋“œ ์‹คํ–‰(ACE)

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

Zip ์Šฌ๋ฆฝ

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

๋”ฐ๋ผ์„œ ์•ฑ์„ ๋นŒ๋“œํ•  ๋•Œ ์•…์˜์ ์ธ ์ œ3์ž๋กœ๋ถ€ํ„ฐ React Native ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ณดํ˜ธํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. React Native์—๋Š” ๋ฏผ๊ฐํ•œ ์ •๋ณด์˜ ์•ˆ์ „ํ•œ ์ €์žฅ์„ ์ง€์›ํ•˜๋Š” ๋‚ด์žฅ๋œ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์ง€ ์•Š์ง€๋งŒ, Android๋‚˜ iOS ํ”Œ๋žซํผ ๋ชจ๋‘์—์„œ ์•ฑ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ์†”๋ฃจ์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์—์„œ ์ด๋“ค ์ค‘ ์ผ๋ถ€์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์•ฑ๊ณผ ์„œ๋ฒ„๊ฐ„ ์—ฐ๊ฒฐ ๋ณด์•ˆ

React Native์—์„œ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ„์˜ ํ†ต์‹ ์€ ์˜คํ”ˆ ํ”Œ๋žซํผ์ด ๊ฐ€์ง„ ๋ณด์•ˆ ์ทจ์•ฝ์  ๋•Œ๋ฌธ์— ๋ฐ˜๋“œ์‹œ ๋ณด์•ˆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ„์˜ ํ†ต์‹ ์— ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์›น ์„œ๋น„์Šค๋Š” HTTP ๊ธฐ๋ฐ˜ REST ์ž…๋‹ˆ๋‹ค. HTTPS ์—ฐ๊ฒฐ์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋‘ ๊ฐ€์ง€๋Š” ํ•ธ๋“œ ์…ฐ์ดํ‚น ์ค‘์— ์„œ๋ฒ„์—์„œ ์ œ๊ณตํ•˜๋Š” ์œ ํšจํ•œ ์ธ์ฆ์„œ์™€ ์ „์†ก ์ค‘์— ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”์— ์‚ฌ์šฉ๋˜๋Š” ์•”ํ˜ธ์ž…๋‹ˆ๋‹ค. ์ธ์ฆ์„œ๋Š” ์„œ๋ฒ„์˜ ID๋ฅผ ์ฆ๋ช…ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ์— ์ €์žฅ๋œ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์ธ์ฆ ๊ธฐ๊ด€(CA)์—์„œ ์„œ๋ช…ํ•œ ์œ ํšจํ•œ ์ธ์ฆ์„œ๋ฅผ ์„œ๋ฒ„๊ฐ€ ์ œ๊ณตํ•  ์ˆ˜ ์—†๋‹ค๋ฉด ์—ฐ๊ฒฐ์ด ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ๊ณต๊ฒฉ์ž๊ฐ€ ์‚ฌ์šฉ์ž ๋””๋ฐ”์ด์Šค์— ์•…์˜์ ์ธ ๋ฃจํŠธ CA ์ธ์ฆ์„œ๋ฅผ ์„ค์น˜ํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ณต๊ฒฉ์ž๊ฐ€ ์„œ๋ช…ํ•œ CA ์ธ์ฆ์„œ๋ฅผ ์‹ ๋ขฐํ•˜๋„๋ก ์†์ด๊ฑฐ๋‚˜ ๋””๋ฐ”์ด์Šค์— ์ €์žฅ๋œ CA๋ฅผ ์™„์ „ํžˆ ์†์ƒ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์•…์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ž๊ฒฉ ์ฆ๋ช…์˜ ์ธ์ฆ ๋ถˆ์ผ์น˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ, ๋ณ„๋„์˜ ์ฝ”๋“œ ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ง„ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜์—ญ ์†์„ฑ์„ ํ• ๋‹นํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์„ ์œ ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์‘๋‹ต ๋ฉ”์ปค๋‹ˆ์ฆ˜๊ณผ ์˜์—ญ ์†์„ฑ ์‚ฌ์ด์˜ ์ž‘์€ ๋ถˆ์ผ์น˜ ์กฐ์ฐจ๋„ ์•ฑ์˜ ๋ณด์•ˆ์„ ์†์ƒ์‹œํ‚ค๊ณ  ๊ถŒํ•œ์ด ์—†๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ์•ก์„ธ์Šค๋ฅผ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย 

React Native์—์„œ SSL ํ”ผ๋‹(Pinning)

SSL(Secure Sockets Layer)์€ ๋„คํŠธ์›Œํ‚น ์ปดํ“จํ„ฐ๊ฐ„์— ์ธ์ฆ ๋ฐ ์•”ํ˜ธํ™”๋œ ๋งํฌ๋ฅผ ์„ค์ •ํ•˜๋Š” ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. TLS(Transport Layer Security) ํ”„๋กœํ† ์ฝœ์€ 1999๋…„ SSL์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ์˜€์ง€๋งŒ ์ข…์ข… โ€œSSLโ€ ๋˜๋Š” โ€œSSL/TLSโ€๋กœ ํ†ต์นญ๋ฉ๋‹ˆ๋‹ค. SSL ํ”ผ๋‹์€ SSL ํ•ธ๋“œ ์…ฐ์ดํ‚น ํ›„์—๋„ ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ์„œ๋ฒ„ ์ธ์ฆ์„œ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ์ˆ ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์ธ์ฆ์„œ ๋ชฉ๋ก์€ ๋Ÿฐํƒ€์ž„ ๋™์•ˆ ์„œ๋ฒ„ ์ธ์ฆ์„œ์™€ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋„๋ก ํด๋ผ์ด์–ธํŠธ ์•ฑ์— ๋‚ด์žฅ๋ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ธ์ฆ์„œ๊ฐ€ ๋กœ์ปฌ ์ธ์ฆ์„œ ๋ชฉ๋ก๊ณผ ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด ์—ฐ๊ฒฐ์ด ์ฆ‰์‹œ ์ค‘๋‹จ๋˜๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ์„œ๋ฒ„๋กœ ์ „์†ก๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋ฒ„์—๋งŒ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ณ ์ •๋œ ์ธ์ฆ์„œ๊ฐ€ ๋งŒ๋ฃŒ๋˜๋ฉด ์ดํ›„ ์ธ์ฆ์„œ๋Š” ๋ฆด๋ฆฌ์Šค ์ „์— ํด๋ผ์ด์–ธํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๊ณ ์ •์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์—…๋ฐ์ดํŠธ๋œ ์ธ์ฆ์„œ๊ฐ€ ๊ณ ์ •๋˜์ง€ ์•Š์œผ๋ฉด, ํ•ด๋‹น ์ธ์ฆ์„œ๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ ์ธ์‹๋˜์ง€ ์•Š์œผ๋ฉฐ ์ถ”๊ฐ€ ํ†ต์‹ ์ด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ โ€œ์•ฑ ๋ธŒ๋ฆญํ‚น(App Bricking)โ€์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. SSL ํ”ผ๋‹์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ „์ฒด ์ธ์ฆ์„œ๋ฅผ ๊ณ ์ •ํ•˜๊ฑฐ๋‚˜ ํ•ด์‹œ๋œ ํ‚ค๋งŒ ๊ณ ์ •ํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ›„์ž๋Š” ์—…๋ฐ์ดํŠธ๋œ ์ธ์ฆ์„œ์— ์„œ๋ช…ํ•˜๊ณ  ๋ธŒ๋ฆญํ‚น ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š”๋ฐ ๋™์ผํ•œ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋” ๋ฐ”๋žŒ์งํ•œ ์˜ต์…˜์ž…๋‹ˆ๋‹ค.

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

1. ๋„คํŠธ์›Œํฌ ๋ณด์•ˆ ๊ตฌ์„ฑ

์ด๊ฒƒ์€ Android์—์„œ ์ธ์ฆ์„œ๋ฅผ ํ”ผ๋‹ํ•˜๋Š” ๊ธฐ๋ณธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์ด๋ฉฐ ์ฝ”๋”ฉ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์œ ์ผํ•œ ๊ฑธ๋ฆผ๋Œ์€ Android N ์ด์ƒ์—์„œ๋งŒ ์ง€์›๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” ์•ฑ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ ๋„ ๋„๋ฉ”์ธ ๋ฐ ์•ฑ์— ํŠน์ •ํ•œ ๋ณด์•ˆ ๊ตฌ์„ฑ ํŒŒ์ผ์˜ ๋ณด์•ˆ ์„ค์ •์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ตฌ์„ฑ์€ res\xml ๋””๋ ‰ํ† ๋ฆฌ์— ์ƒ์„ฑ๋œ XML ํŒŒ์ผ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. XML ํŒŒ์ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ ์–ธ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<?xml version=”1.0″ encoding=”utf-8″?>
<manifest … >
ย  ย  <application
ย  ย  ย  ย  android:networkSecurityConfig=”@xml/network_security_config”
ย  ย  ย  ย  ย … >
ย ย ย ย ย ย ย …
ย ย ย </application>
</manifest>

์ด์ œ ย <base-config> ๋ฐ<domain-config>ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ๋ณด์•ˆ ํŒŒ์ผ์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • <base-config>๋Š” ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•˜๋Š” ๋„๋ฉ”์ธ์— ๊ด€๊ณ„์—†์ด ์ „์ฒด ์•ฑ์— ์ ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ตฌ์„ฑ์„ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
  • <domain-config>๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์„ ํƒํ•œ ๋„๋ฉ”์ธ์— ํŠน์ •ํ•œ ๊ทœ์น™์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

2. TrustManager

TrustManager๋Š” ํ”ผ์–ด๊ฐ€ ์ œ๊ณตํ•œ ์ž๊ฒฉ ์ฆ๋ช…์„ ์•ฑ์—์„œ ํ—ˆ์šฉํ•  ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. javax.net.ssl์˜ ์ด ๊ธฐ์ˆ ์€ ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ํ†ตํ•ด ์ธ์ฆ์„œ ํ”ผ๋‹์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ฐ€๊ธ‰์ ์ด๋ฉด ์ฃผ์„ ์ค„์ด ์—†๋Š” PEM ๋˜๋Š” DER ํ˜•์‹์˜ ์ธ์ฆ์„œ ํŒŒ์ผ์„ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  • ์ธ์ฆ์„œ๋กœ KeyStore ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

val resource_stream = resources.openRawResource(R.raw.cert)
val key_store_type = KeyStore.getDefaultType()
val key_store = KeyStore.getInstance(key_store_type)
key_store.load(resource_stream, null)

  • TrustManager ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

val trust_manager_algorithm = TrustManagerFactory.getDefaultAlgorithm()
val trust_manager_factory = TrustManagerFactory.getInstance(trust_manager_algorithm)
trust_manager_factory.init(keyStore)

  • ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„๋Š” TLS ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜์—ฌ SSL ์ปจํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ TrustManager๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณด์•ˆ SSL ์—ฐ๊ฒฐ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.ย 

val ssl_context = SSLContext.getInstance(“TLS”)
ssl_context.init(null, trust_manager_factory.trustManagers, null)
val url = URL(“http://www.secureexample.com/“)
val url_connection = url.openConnection() as HttpsURLConnection
url_connection.sslSocketFactory = ssl_context.socketFactory

3. OkHttp ๋ฐ ์ธ์ฆ์„œ ํ”ผ๋‹

์ด๊ฒƒ์€ Square์˜ ์œ ๋ช…ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์‚ฌ์šฉ์ด ์‰ฌ์šฐ๋ฉฐ, ๋„คํŠธ์›Œํ‚น์„ ์œ„ํ•ด Retrofit์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์‹œ์ž‘ํ•˜๋ ค๋ฉด OktHtp CertificatePinner ๋นŒ๋”์—์„œ ์ธ์ฆ์„œ ํ”ผ๋‹ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ํ•ด๋‹น ๋„๋ฉ”์ธ๊ณผ ์ง€๋ฌธ์„ ์—ฌ๊ธฐ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„๋Š” ๋นŒ๋”๋ฅผ OkHttp ํด๋ผ์ด์–ธํŠธ์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

val certificatePinner = CertificatePinner.Builder()
ย ย ย ย ย ย ย .add(
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย “www.secureexample.com”,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย “sha256/7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=”
ย ย ย ย ย ย ย ).build()
val okHttpClient = OkHttpClient.Builder()
ย ย ย ย ย ย ย .certificatePinner(certificatePinner)
ย ย ย ย ย ย ย .build()

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

react-native-pinch ๋˜๋Š” react-native-ssl-pinning๊ณผ ๊ฐ™์€ React Native ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ React Native ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ SSL ํ”ผ๋‹์„ ๊ตฌํ˜„ํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋„ค์ดํ‹ฐ๋ธŒ ํ”Œ๋žซํผ์—์„œ์˜ ๊ตฌํ˜„์€ Android์™€ iOS์—์„œ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๋Š”ย openssl s_client -showcerts -servername <domain(i.e. google.com)> -connect <domain(i.e. google.com)>:443 ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ๋„๋ฉ”์ธ์— ๋Œ€ํ•œ ์ธ์ฆ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ถœ๋ ฅ์€ ์ฒด์ธ์˜ ์ „์ฒด ์ธ์ฆ์„œ(์ƒ๋‹จ์˜ ๋ฆฌํ”„ ์ธ์ฆ์„œ์™€ ํ•˜๋‹จ์˜ ๋ฃจํŠธ ์ธ์ฆ์„œ)์ž…๋‹ˆ๋‹ค.

iOS ํ”Œ๋žซํผ์˜ ๊ฒฝ์šฐ ๋‚ด์žฅ ์Šคํฌ๋ฆฝํŠธ(get_pin_from_certificate.py)๊ฐ€ ํฌํ•จ๋œ TrustKit์ด ์ผ๋ฐ˜์ ์œผ๋กœ SSL ํ”ผ๋‹์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  $python get_pin_from_certificate.py <your .pem file> ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ณต๊ฐœํ‚ค ํ•ด์‹œ๋ฅผ ๊ฒ€์ƒ‰ํ•˜์‹ญ์‹œ์˜ค.

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

React Native ์ €์žฅ์†Œ ๋ณด์•ˆ

๊ฐœ๋ฐœ์ž๋Š” ์ข…์ข… ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด๋ถ€์— ์˜๊ตฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. React Native๋Š” Async-storage, sqlite, pouchdb ๋ฐ realm๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.ย 

๋‹ค์Œ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ณด์•ˆ ๊ณ„์ธต์„ ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ํ”Œ๋Ÿฌ๊ทธ์ธ์ž…๋‹ˆ๋‹ค.

  • SQLite : ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. SQLCipher๋Š” SQLite ์•”ํ˜ธํ™”๋ฅผ ์œ„ํ•ด ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ์˜คํ”ˆ ์†Œ์Šค ํ™•์žฅ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. ์•”ํ˜ธ๋Š” ํ‚ค ์—†์ด๋Š” ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์—†๋Š” 256๋น„ํŠธ AES๋กœ ์•”ํ˜ธํ™”๋ฉ๋‹ˆ๋‹ค. React Native์—๋Š” SQLCipher๋ฅผ ์ œ๊ณตํ•˜๋Š”ย react-native-sqlcipher-2 (ORM ๊ณต๊ธ‰์ž๋กœ pouchdb ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ)์™€ react-native-sqlcipher-storage (Cordova ๊ตฌํ˜„ ๊ธฐ๋ฐ˜) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Realm : SQLite์— ๋น„ํ•ด ๋” ๋น ๋ฅธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ด๋ฉฐ ๊ธฐ๋ณธ์ ์œผ๋กœ ์•”ํ˜ธํ™”๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ AES256 ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜๋ฉฐ ์•”ํ˜ธํ™”๋Š” SHA-2 HMAC ํ•ด์‹œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

์ด ์™ธ์—๋„ React Native๋Š” iOS ๋ฐ Android๋ฅผ ์œ„ํ•ด์„œ ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋ณธ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์—์„œ ์•ˆ์ „ํ•œ ์ €์žฅ์†Œ๋ฅผ ์ œ๊ณตํ•˜๋Š” React Native ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•ด์„œ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

iOS : KeyChain ์„œ๋น„์Šค

ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ์ •๋ณด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•˜๋Š”๋ฐ ๋„์›€์ด ๋˜๋„๋ก Apple์€ KeyChain ์„œ๋น„์Šค๋ผ๋Š” ๋ณด์•ˆ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ฑ์€ KeyChain์ด๋ผ๋Š” ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ž‘์€ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ์ฒญํฌ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. KeyChain์€ ์ฃผ๋กœ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(SecKeychain ํด๋ž˜์Šค)์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์‚ฝ์ž…๋œ ํ•ญ๋ชฉ (SecKeychainItem ํด๋ž˜์Šค)์˜ ๋‘ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ, ์ธ์ฆ์„œ, ์‹ ์šฉ ์นด๋“œ ์ •๋ณด, ํ† ํฐ ๋“ฑ๊ณผ ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์—ฌ๊ธฐ์— ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. react-native-keychain ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” React Native ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ KeyChain/KeyStore ์•ก์„ธ์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

ํ‚ค ์ฒด์ธ์€ ๋‹ค์Œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์—ฌ React-Native-Keychain-Library๋ฅผ ํ†ตํ•ด์„œ ์‰ฝ๊ฒŒ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
yarn add react-native-keychain

๊ทธ๋Ÿฐ ๋‹ค์Œ, ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ KeyChain์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
react-native link react-native-keychain

MainApplication.java์— ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜์—ฌ ์„ฑ๊ณต์ ์ธ ์—ฐ๊ฒฐ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฐ ๋‹ค์Œ, ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค์‹œ ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
react-native run-ios
react-native run-android

์œ„์˜ ๋‹จ๊ณ„๋ฅผ ์™„๋ฃŒํ•˜๋ฉด react-native-keychain-library๋ฅผ ์•ฑ ๋‚ด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ย setGenericPassword(username, password, [{ accessControl, accessible, accessGroup, service }]) ๋Š” ์‚ฌ์šฉ์ž ์ด๋ฆ„/์•”ํ˜ธ ์กฐํ•ฉ์„ ๋ณด์•ˆ ์ €์žฅ์†Œ์— ์ €์žฅํ•˜๊ณ  ํ•ญ๋ชฉ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ย true ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด ํ•จ์ˆ˜๋Š” ๋ฌธ์ž์—ด ๊ฐ’๋งŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์ฒด ๊ฐ’์ด ์žˆ๋Š” ์ž๊ฒฉ ์ฆ๋ช…์€ JSON.stringify๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

getGenericPassword([{ authenticationPrompt, service }]) ๋Š” ๋ณด์•ˆ ์ €์žฅ์†Œ์—์„œ ์‚ฌ์šฉ์ž ์ด๋ฆ„/์•”ํ˜ธ ์กฐํ•ฉ์„ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ํ•ญ๋ชฉ์ด ์žˆ์œผ๋ฉดย { username, password }๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด false ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ถŒํ•œ ๊ด€๋ จ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๊ธฐ๋Šฅ์ด ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค. setGenericPassword์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ด ํ•จ์ˆ˜๋„ ๋ฌธ์ž์—ด๋งŒ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ฐ์ฒด ํ˜•์‹์˜ ์ž๊ฒฉ ์ฆ๋ช…์€ย JSON.parse๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ด๋ฆ„/์•”ํ˜ธ ์กฐํ•ฉ์€ resetGenericPasswordํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณด์•ˆ ์ €์žฅ์†Œ์—์„œ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ์žฌ์„ค์ • ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

requestSharedWebCredentials() ํ•จ์ˆ˜๋Š” iOS ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ณต์œ  ์›น ์ž๊ฒฉ ์ฆ๋ช…์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ฑ๊ณผ ์„œ๋ฒ„์— ์ถ”๊ฐ€ ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์Šน์ธ๋œ ๊ฒฝ์šฐ { server, username, password }๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๊ฑฐ๋ถ€๋œ ๊ฒฝ์šฐย false ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํ”Œ๋žซํผ์ด ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ๊ณต์œ  ์ž๊ฒฉ ์ฆ๋ช…์ด ์—†๋Š” ๊ฒฝ์šฐ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

setSharedWebCredentials(server, username, password) ๋Š” ๊ณต์œ  ์›น ์ž๊ฒฉ ์ฆ๋ช…์„ ์„ค์ •ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ ํ•จ์ˆ˜๊ฐ€ ์Šน์ธ๋˜๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

setInternetCredentials(server, username, password, [{ accessControl, accessible, accessGroup }]) ํ•จ์ˆ˜๋Š” ์‚ฌ์šฉ์ž ์ด๋ฆ„ ๋ฐ ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ํ•จ๊ป˜ ์„œ๋ฒ„ ์ด๋ฆ„์„ ๋ณด์•ˆ ์ €์žฅ์†Œ์— ์ €์žฅํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

getInternetCredentials(server, [{ authenticationPrompt }])๋Š” ๋ณด์•ˆ ์ €์žฅ์†Œ์—์„œ ์„œ๋ฒ„/์‚ฌ์šฉ์ž ์ด๋ฆ„/์•”ํ˜ธ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.

resetInternetCredentials(server) ํ•จ์ˆ˜๋Š” ๋ณด์•ˆ ์ €์žฅ์†Œ์—์„œ ์„œ๋ฒ„/์‚ฌ์šฉ์ž ์ด๋ฆ„/๋น„๋ฐ€๋ฒˆํ˜ธ ์กฐํ•ฉ์„ ์™„์ „ํžˆ ์ œ๊ฑฐํ•˜๊ณ  ์žฌ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

canImplyAuthentication([{ authenticationType }]) ํ•จ์ˆ˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ์„ค์ •์œผ๋กœ ๋””๋ฐ”์ด์Šค์—์„œ ์ธ์ฆ ์ •์ฑ…์ด ์ง€์›๋˜๋Š”์ง€ ํ™•์ธํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. setter ํ•จ์ˆ˜์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”ย accessControlํ•จ์ˆ˜์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์Šน์ธ๋˜๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.ย 

getSupportedBiometryType() ์€ ๋””๋ฐ”์ด์Šค์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ•˜๋“œ์›จ์–ด ์ƒ์ฒด ์ธ์‹ ์ง€์›์— ๋Œ€ํ•ด ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ์ง€์›๋˜๋Š” ๊ฒฝ์šฐย Keychain.BIOMETRY_TYPE ์—ด๊ฑฐํ˜•์œผ๋กœ ํ™•์ธ๋˜๊ฑฐ๋‚˜ย null์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. Android ๋ฐ iOS ๊ธฐ๊ธฐ ๋ชจ๋‘์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜๋˜๋Š” ์ƒ์ฒด ์ธ์‹ ์œ ํ˜•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

TOUCH_ID (iOS only)
FACE_ID (iOS only)
FINGERPRINT (Android only)

API๋Š” ๋˜ํ•œ KeyChain์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ์ ์„ ๊ฒฐ์ •ํ•˜๋Š” Keychain.ACCESSIBLE ์—ด๊ฑฐํ˜•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์œ ํ˜• ๋ฐ ์ •๋ณด์˜ ๋ฏผ๊ฐ๋„์— ๋”ฐ๋ผ ์˜ต์…˜์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ช‡ ๊ฐ€์ง€ ์˜ต์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

WHEN_UNLOCKED: ๋””๋ฐ”์ด์Šค๊ฐ€ ์ž ๊ธˆ ํ•ด์ œ๋œ ๊ฒฝ์šฐ์—๋งŒ KeyChain์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย 

ALWAYS: KeyChain์„ ํ•ญ์ƒ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์•ˆ์ „ํ•˜์ง€ ์œ ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย 

WHEN_UNLOCKED_THIS_DEVICE_ONLY: KeyChain์€ ํŠน์ • ๋””๋ฐ”์ด์Šค์—์„œ ์ž ๊ธˆ์ด ํ•ด์ œ๋œ ๊ฒฝ์šฐ์—๋งŒ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์—ด๊ฑฐํ˜•์— ์†ํ•œ ํ•ญ๋ชฉ์€ ๋‹ค๋ฅธ ๋””๋ฐ”์ด์Šค๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

ALWAYS_THIS_DEVICE_ONLY: KeyChain์€ ๋””๋ฐ”์ด์Šค์˜ ์ž ๊ธˆ ํ•ด์ œ ์—ฌ๋ถ€์— ๊ด€๊ณ„์—†์ด ํŠน์ • ๋””๋ฐ”์ด์Šค์— ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

AFTER_FIRST_UNLOCK: ๋””๋ฐ”์ด์Šค๋ฅผ ๋‹ค์‹œ ์‹œ์ž‘ํ•œ ํ›„ ์ž ๊ธˆ์„ ํ•ด์ œํ•˜์ง€ ์•Š์œผ๋ฉด KeyChain์˜ ๋ฐ์ดํ„ฐ์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: ํŠน์ • ๋””๋ฐ”์ด์Šค์—์„œ ์ฒ˜์Œ ์ž ๊ธˆ์„ ํ•ด์ œํ•œ ํ›„์— ๋งŒ โ€‹โ€‹KeyChain์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋””๋ฐ”์ด์Šค๊ฐ€ ์ž ๊ธˆ ํ•ด์ œ๋˜์ง€ ์•Š์œผ๋ฉด ๋‹ค์‹œ ์‹œ์ž‘ํ•œ ํ›„์—๋„ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์•„๋งˆ๋„ ๊ฐ€์žฅ ์•ˆ์ „ํ•œ ์˜ต์…˜ ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ์†์„ฑ์ด ์žˆ๋Š” ํ•ญ๋ชฉ๋„ ๋‹ค๋ฅธ ๋””๋ฐ”์ด์Šค๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

WHEN_PASSCODE_SET_THIS_DEVICE_ONLY: KeyChain์˜ ๋ฐ์ดํ„ฐ๋Š” ๋””๋ฐ”์ด์Šค๊ฐ€ ์ž ๊ธˆ ํ•ด์ œ๋œ ๊ฒฝ์šฐ์—๋งŒ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜ต์…˜์€ ๋””๋ฐ”์ด์Šค์— ์•”ํ˜ธ๊ฐ€ ์„ค์ •๋œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Keychain.ACCESS_CONTROL enum์—ด๊ฑฐํ˜•์€ KeyChain์˜ ํ•ญ๋ชฉ์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ๋Š” ์‚ฌ๋žŒ์„ ๊ฒฐ์ •ํ•˜๋Š” ์˜ต์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ KeyChain ํ•ญ๋ชฉ์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์ œ์–ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

USER_PRESENCE Touch ID ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ํ•ญ๋ชฉ์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
BIOMETRY_ANY ์˜ค์ง ๋“ฑ๋ก๋œ ๋ชจ๋“  ์†๊ฐ€๋ฝ์˜ Touch ID ๋งŒ์œผ๋กœ ํ•ญ๋ชฉ์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
BIOMETRY_CURRENT_SET ํ˜„์žฌ ๋“ฑ๋ก๋œ ์†๊ฐ€๋ฝ์— ๋Œ€ํ•ด์„œ๋งŒ Touch ID๋กœ ํ•ญ๋ชฉ์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
DEVICE_PASSCODE ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ํ•ญ๋ชฉ์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
APPLICATION_PASSWORD ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™” ํ‚ค ์ƒ์„ฑ์„ ์œ„ํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ œ๊ณต ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ œ์•ฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
BIOMETRY_ANY_OR_DEVICE_PASSCODE ๋“ฑ๋ก๋œ ๋ชจ๋“  ์†๊ฐ€๋ฝ์˜ Touch ID ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ญ๋ชฉ์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE ํ˜„์žฌ ๋“ฑ๋ก๋œ ์†๊ฐ€๋ฝ์˜ Touch ID ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ์— ๋Œ€ํ•ด์„œ๋งŒ ํ•ญ๋ชฉ์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ๋Š” ์ œ์•ฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

Android: ์•ˆ์ „ํ•œ SharedPreferences

Android์—์„œ๋Š” ํ‚ค-๊ฐ’ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ์— ํ•ด๋‹นํ•˜๋Š” ๊ฒƒ์„ย SharedPreferences๋ผ๊ณ  ๋ช…๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์˜๊ตฌ์ ์ธย  ํ‚ค-๊ฐ’ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ์œ ํ˜•์„ ์ €์žฅํ•˜๊ณ  ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ย SharedPreferences ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ผ๋ฐ˜ ํ…์ŠคํŠธ๋กœ ์ €์žฅํ•˜๋ฏ€๋กœ ์ž ์žฌ์ ์ธ ์œ„ํ˜‘์œผ๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณดํ˜ธํ•˜๋ ค๋ฉด ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Android Keystore๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SharedPreferences ์— ๋Œ€ํ•œ ์ž์ฒด ์•”ํ˜ธํ™” ๋ž˜ํผ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋” ์‰ฝ๊ณ  ์•ˆ์ „ํ•œ ์˜ต์…˜์€ ๋ณด๋‹ค ๊ฐ„๋‹จํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด UI๋ฅผ ์ œ๊ณตํ•˜๋Š” AndroidX ๋ณด์•ˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์•”ํ˜ธํ™”๋œ ๊ณต์œ  ํ™˜๊ฒฝ ์„ค์ •์„ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. AndroidX ๋ณด์•ˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•œ ์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๋Š” ๋ชจ๋“ˆ ์ˆ˜์ค€ build.gradle ํŒŒ์ผ์— ์ข…์†์„ฑ implementation”androidx.security:security-crypto:1.0.0-alpha03″ ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ˜„์žฌ ์•ŒํŒŒ ๋‹จ๊ณ„์— ์žˆ์œผ๋ฏ€๋กœ API์˜ ์ผ๋ถ€๊ฐ€ ํ–ฅํ›„ ๋ฒ„์ „์—์„œ ๋ณ€๊ฒฝ๋˜๊ฑฐ๋‚˜ ์ œ๊ฑฐ๋  ์ˆ˜ ์žˆ์Œ์„ ์—ผ๋‘์— ๋‘์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ข…์†์„ฑ์„ ์ถ”๊ฐ€ํ•œ ํ›„ ์•”ํ˜ธํ™” ๋งˆ์Šคํ„ฐ ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ Android Keystore์— ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
EncryptedSharedPreferences ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค ์œ„์น˜์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๋ฐฐ์น˜ํ•˜๋ฉด ์‰ฝ๊ฒŒ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec).

๊ธฐ๋ณธ ์‚ฌ์–‘์ธ AES256_GCM_SPEC์ด ๋งˆ์Šคํ„ฐ ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์ด ์‚ฌ์–‘์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์ง€๋งŒ ํ‚ค ์ƒ์„ฑ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ KeyGenParameterSpec์„ ์ œ๊ณตํ•˜๋Š” ์˜ต์…˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ SharedPreferencesย ๋ฅผ ๊ฐ์‹ธ๊ณ  ์•”ํ˜ธํ™”๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” EncryptedSharedPreferences ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. SharedPreferences์˜ ์ธ์Šคํ„ด์Šค๋Š”ย Context#getSharedPreferencesย ๋˜๋Š”ย Activity#getPreferences ์—์„œ ์ง์ ‘ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์ง€๋งŒย EncryptedSharedPreferences์˜ ์ž์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ๋ฅผ ์ฐธ๊ณ ํ•˜์‹ญ์‹œ์˜ค.

val sharedPreferences = EncryptedSharedPreferences.create(
ย ย ย “shared_preferences_filename”,
ย ย ย masterKeyAlias,
ย ย ย context,
ย ย ย EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
ย ย ย EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

SharedPreferencesํŒŒ์ผ๊ณผ ์ด์ „์— ์ƒ์„ฑ๋œ masterKeyAlias ๊ทธ๋ฆฌ๊ณ ย Context์˜ ์ด๋ฆ„์„ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ ๋‘ ์ธ์ˆ˜๋Š” ๊ฐ๊ฐ ํ‚ค์™€ ๊ฐ’์„ ์•”ํ˜ธํ™”ํ•ฉ๋‹ˆ๋‹ค. ย EncryptedSharedPreferences ์ธ์Šคํ„ด์Šค๋Š”ย SharedPreferences ์™€ ๋˜‘๊ฐ™์ด ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’์„ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
val sharedPreferences = EncryptedSharedPreferences.create(
ย ย ย “shared_preferences_filename”,
ย ย ย masterKeyAlias,
ย ย ย context,
ย ย ย EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
ย ย ย EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
// storing a value
sharedPreferences
ย ย ย .edit()
ย  ย .putString(“some_key”, “some_data”)
ย ย ย .apply()
// reading a value
sharedPreferences.getString(“some_key”, “some_default_value”) // -> “some_data”

Android Keystore

๊ฐœ๋ฐœ์ž๋Š” Android Keystore๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•”ํ˜ธํ™” ํ‚ค๋ฅผ ๋””๋ฐ”์ด์Šค์—์„œ ์ถ”์ถœ๋˜์ง€ ์•Š๋„๋ก ๋ณดํ˜ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ์‚ฌ์šฉ์ž ์ธ์ฆ์„ ์š”์ฒญํ•˜๊ฑฐ๋‚˜ ํŠน์ • ์•”ํ˜ธํ™” ๋ชจ๋“œ์—์„œ๋งŒ ํ‚ค ์‚ฌ์šฉ์„ ์ œํ•œํ•˜๋Š” ๋“ฑ์˜ ํ‚ค ์‚ฌ์šฉ ๋ฐฉ๋ฒ•๊ณผ ์‹œ๊ธฐ๋ฅผ ์ œ์–ดํ•˜๋Š” โ€‹โ€‹๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Android Keystore์˜ ํ‚ค๋ฅผ ๋ณดํ˜ธํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ์˜ ๋ณด์•ˆ ์กฐ์น˜๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • ํ‚ค ์ž๋ฃŒ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋กœ์„ธ์Šค์— ๋“ค์–ด ๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์•ฑ์˜ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์†์ƒ๋œ ๊ฒฝ์šฐ์—๋„ ํ•ด์ปค๊ฐ€ ํ‚ค ์ž๋ฃŒ๋ฅผ ์ถ”์ถœํ•  ์ˆ˜ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • ํ‚ค ์ž๋ฃŒ๋Š” ์žฅ์น˜์˜ TEE(Trusted Execution Environment) ๋˜๋Š” SE(Secure Element)์™€ ๊ฐ™์€ ๋ณด์•ˆ ํ•˜๋“œ์›จ์–ด์— ๋ฐ”์ธ๋”ฉ๋ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณด์•ˆ ํ•˜๋“œ์›จ์–ด ์™ธ๋ถ€๋กœ ํ‚ค ์ž๋ฃŒ๊ฐ€ ๋…ธ์ถœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ณต๊ฒฉ์ž๊ฐ€ Keystore์˜ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋”๋ผ๋„ ๋””๋ฐ”์ด์Šค์—์„œ ์ถ”์ถœํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์„ ํ™•์ธํ•˜๋ ค๋ฉด ํ‚ค์— ๋Œ€ํ•œ KeyInfo๋ฅผ ์–ป๊ณ ย KeyInfo.isInsideSecurityHardware()์˜ ๋ฐ˜ํ™˜ ๊ฐ’์„ ์ฐพ์œผ์‹ญ์‹œ์˜ค.

Android ๋””๋ฐ”์ด์Šค์—์„œ ํ‚ค์˜ ๋ฌด๋‹จ ์‚ฌ์šฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์•ฑ์€ ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ๋™์•ˆ ์Šน์ธ๋œ ์‚ฌ์šฉ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜จ ํ›„์—๋Š” ๊ถŒํ•œ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. Android Keystore์—์„œ ์ง€์›๋˜๋Š” ํ‚ค ์‚ฌ์šฉ ์Šน์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ถ„๋ฅ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์•”ํ˜ธํ™” : ์ธ์ฆ๋œ ํ‚ค ์•Œ๊ณ ๋ฆฌ์ฆ˜, ๋ธ”๋ก ๋ชจ๋“œ, ํŒจ๋”ฉ ์ฒด๊ณ„, ์ž‘์—… ๋˜๋Š” ๋ชฉ์ (์•”ํ˜ธํ™”, ๋ณตํ˜ธํ™”, ์„œ๋ช…, ํ™•์ธ) ๋ฐ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์ด์ œ์ŠคํŠธ
  • ์ž„์‹œ ์œ ํšจ์„ฑ ๊ฐ„๊ฒฉ : ํ‚ค ์‚ฌ์šฉ ๊ถŒํ•œ์ด ๋ถ€์—ฌ๋œ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ์ธ์ฆ : ์‚ฌ์šฉ์ž๋Š” ํ‚ค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„ ๋งŒํผ ์ตœ๊ทผ์— ์ธ์ฆ์„ ๋ฐ›์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Android OS์—์„œ ์•ฑ์„ ์„ค์น˜ํ•˜๋ ค๋ฉด ์„œ๋ช…์ด ๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ณด์•ˆ ์ธ์ฆ์„œ์šฉ ์Šคํ† ๋ฆฌ์ง€ ์‹œ์Šคํ…œ์ธ Keystore๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ตฌ๊ธ€ ํ”Œ๋ ˆ์ด ์Šคํ† ์–ด์™€ ๊ฐ™์€ ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค์— ์•ฑ์„ ๋ฐฐํฌํ•˜๊ธฐ ์ „์— ๊ณต๊ฐœํ‚ค ์ธ์ฆ์„œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ APK์— ์„œ๋ช…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. APKย  ์„œ๋ช…์„ ํ†ตํ•ด์„œ ์ดํ›„ ๋™์ผํ•œ ์•ฑ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์•…์˜์ ์ธ ์ œ3์ž๊ฐ€ ์•„๋‹Œ ๊ฐœ๋ฐœ์ž์— ์˜ํ•ด ์ˆ˜ํ–‰๋จ์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋™์ผํ•œ ์•ฑ์ด ํ–ฅํ›„ ๋‹ค๋ฅธ ํ‚ค๋กœ ์„œ๋ช…๋œ ๊ฒฝ์šฐ์— ์•ฑ์„ ๋ฐฐํฌํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐฐํฌ์ „์— ์•ฑ์˜ ์ˆ˜๋ช…์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•ฑ์˜ ์ˆ˜๋ช…์ด ๊ฒฐ์ •๋˜๋ฉด keytool ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Keystore๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.keytool์€ Java JDK ์„ค์น˜ ํ›„์— ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. keytool ์—๋Š” ์—ฌ๋Ÿฌ ๋ช…๋ น์ด ์žˆ์œผ๋ฉฐ Android ์•ฑ์—์„œ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ช…๋ น์€ -genkeypair์ด๋ฉฐ ์ถ•์•ฝํ˜•์€ -genkey์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” -genkey

์˜ต์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

-alias ย  ย  ย  ย  ย  ย  ย  ํ‚ค์Œ ๋ณ„์นญ ์ด๋ฆ„
-keyalg ย  ย  ย  ย  ย  ย  ย  ํ‚ค์Œ์„ ์ƒ์„ฑํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜
-keysize ย  ย  ย  ย  ย  ย  ย  ํ‚ค์Œ ํฌ๊ธฐ(๋น„ํŠธ)
-validity ย  ย  ย  ย  ย  ย  ย  ํ‚ค์Œ ์œ ํšจ ๊ธฐ๊ฐ„(์ผ)

 

์˜ˆ๋ฅผ ๋“ค์–ด, keytool -genkey -v -keystore release.keystore -alias example -keyalg RSA -keysize 2048 -validity 12000 ๋ช…๋ น์€ ย release.keystore ์ด๋ผ๋Š” ๋ณ„์นญ ์ด๋ฆ„๊ณผ 12,000์ผ์˜ ์œ ํšจ ๊ธฐ๊ฐ„์˜ RSA-2048 ๊ณต๊ฐœ/๊ฐœ์ธํ‚ค ์Œ์ด ์žˆ๋Š” release.keystore๋ผ๋Š” Keystore ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. APK์— ์„œ๋ช…ํ•˜๋ ค๋ฉด Keystore์™€ ํ‚ค ๋ชจ๋‘์— ๋Œ€ํ•œ ๊ฐ•๋ ฅํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. APK๋Š” ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ์„œ๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Gradle๋กœ ์„œ๋ช…

gradle์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋นŒ๋“œํ•˜๊ธฐ ์œ„ํ•ด ๊ฐœ๋ฐœ์ž๋Š” android.signingConfig๋ฅผ ๋งŒ๋“  ๋‹ค์Œ ํ•˜๋‚˜ ์ด์ƒ์˜ android.buildTypes๊ณผ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. gradle ๋นŒ๋“œ ์Šคํฌ๋ฆฝํŠธ์— ํ‚ค ์ €์žฅ์†Œ ์ด๋ฆ„๊ณผ ๋ณ„์นญ์„ ์ž…๋ ฅํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

android {
ย ย ย ย signingConfigs {
ย ย ย ย ย ย ย ย release {
ย ย ย ย ย ย ย ย ย ย ย ย storeFile config.keystore
ย ย ย ย ย ย ย ย ย ย ย ย storePassword storePassword
ย ย ย ย ย ย ย ย ย ย ย ย keyAlias example
ย ย ย ย ย ย ย ย ย ย ย ย keyPassword keyPassword
ย ย ย ย ย ย ย ย }
ย ย ย ย }
ย ย ย ย buildTypes {
ย ย ย ย ย ย ย ย release {
ย ย ย ย ย ย ย ย ย ย ย ย signingConfig signingConfigs.release
ย ย ย ย ย ย ย ย }
ย ย ย ย }
ย ย }

  • ์ˆ˜๋™์œผ๋กœ ์„œ๋ช…

APK์— ์ˆ˜๋™์œผ๋กœ ์„œ๋ช…ํ•˜๋ ค๋ฉด build-tools ๋ฒ„์ „ 24.0.3 ์ด์ƒ์˜ ๊ฒฝ์šฐ {ANDROID_SDK_DIRECTORY}/build-tools/{BUILD_TOOLS_VERSION}/apksigner ๊ฒฝ๋กœ์— ์œ„์น˜ํ•œย apksigner ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ด ๋„๊ตฌ๋Š” ๊ณต๊ฐœํ‚ค ์ธ์ฆ์„œ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด Keystore์— ์ €์žฅ๋œ ๊ณต๊ฐœ/๊ฐœ์ธํ‚ค ์Œ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๊ฐœ์ธํ‚ค๊ฐ€ ์œ ์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์—ฐ๊ฒฐ๋œ APK์— ํ•ด๋‹น ์ธ์ฆ์„œ๋ฅผ ์ฒจ๋ถ€ํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ์•ฑ์˜ ์••์ถ•๋˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์˜คํ”„์…‹์—์„œ ์‹œ์ž‘๋˜๋„๋ก APK๋ฅผย zipalignํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ๊ตฌ๊ธ€ ํ”Œ๋ ˆ์ด ์Šคํ† ์–ด์—๋Š”ย zipalign๋œ APK๋ฅผ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค.ย 

zipalign -v -p 4 app-flavor-buildtype-unsigned.apk app-flavor-buildtype-unsigned-aligned.apk

  • APK๊ฐ€ ์••์ถ•๋œ ํ›„ apksigner๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ช…ํ•ฉ๋‹ˆ๋‹ค.

apksigner sign –ks release.keystore –out app-flavor-buildtype.apk app-flavor-buildtype-unsigned-aligned.apk

  • ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ช…๋ น์ค„์—์„œ Keystore์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. Keystore ๋ฐ ํ‚ค์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. –ks-pass ๋ฐ –key-pass ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ๋ณ„์ ์œผ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๋ผ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์˜ต์…˜์„ ๋”ฐ๋ผ stdin์„ ํ†ตํ•ด์„œ ๋ช…๋ น์ค„๋กœ๋ถ€ํ„ฐ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค.

apksigner sign –ks example.keystore –ks-pass stdin –key-pass stdin –out app-signed.apk app.apk

React Native API ๋ณด์•ˆ ๋ฌธ์ œ ์กฐ์‚ฌ

API๋Š” ์ฃผ๋กœ JSON ํ˜•์‹์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ํŠน์ • ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ์„ธํŠธ์ž…๋‹ˆ๋‹ค. API์—์„œ ๋ฐ์ดํ„ฐ์— ์•ก์„ธ์Šคํ•œ๋‹ค๋Š” ๊ฒƒ์€ API ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์—”๋“œํฌ์ธํŠธ์— ์•ก์„ธ์Šคํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. React API๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋‹ค๋ฅธ ํ”Œ๋žซํผ ๋ฐ ์„œ๋น„์Šค ๊ฐ„์˜ ํ†ต์‹ ์„ ์„ค์ •ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋‹ค๋ฅธ ๋””๋ฐ”์ด์Šค๋‚˜ ์•ฑ์ด ์„ค์น˜๋œ ํŠน์ • ๋””๋ฐ”์ด์Šค๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ์˜ต์…˜๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ API๋Š” ์ธ์ฆ์ด ์ ์ ˆํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๊ฒฐํ•จ์ด ์žˆ๋Š” ๊ฒฝ์šฐ, ์•ฑ์ด ๋ณด์•ˆ ์œ„ํ˜‘ ๋˜๋Š” MITM(man-in-the-middle) ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. XSS ๋ฐ SQL ์ธ์ ์…˜(SQLi) ๊ณต๊ฒฉ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. React Native์—์„œ API๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ํ•„์š”ํ•œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์ •๋ณด๋ฅผ ์ž๋™์œผ๋กœ ๋ฌธ์„œํ™” ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ API ๋ณด์•ˆ ๊ด€๋ จ ์žฅ์• ๋ฅผ ์ค„์ด๊ฑฐ๋‚˜ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ฐ๊ฐ์˜ API ์Šคํ‚ค๋งˆ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  API ํ˜ธ์ถœ ๋ช…๋ น์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
  • ์•…์„ฑ์ฝ”๋“œ ์ธ์ ์…˜์ด๋‚˜ ๋ณด์•ˆ ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์Šคํ‚ค๋งˆ์˜ ์ฃผ๊ธฐ์ ์ด๊ณ  ์‹œ๊ธฐ ์ ์ ˆํ•œ ๊ฒ€์ฆ
  • SSL/TLS ํ”ผ๋‹์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์•ˆ์ „ํ•จ์„ ๋ณด์žฅ

DDoS ๊ณต๊ฒฉ์— ๋Œ€ํ•œ React Native ๋ณด์•ˆ

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

  • UDP ํ”Œ๋Ÿฌ๋”ฉ โ€“ ํ˜ธ์ŠคํŠธ ์„œ๋น„์Šค์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์—†๊ฒŒ๋ฉ๋‹ˆ๋‹ค.
  • ICMP ํ”Œ๋Ÿฌ๋”ฉ โ€“ React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ƒ๋‹นํžˆ ๋Š๋ฆฌ๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  • SYN ํ”Œ๋Ÿฌ๋”ฉ โ€“ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋น„์Šค๋ฅผ ์‰ฝ๊ฒŒ ๊ณต๊ฒฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.ย 
  • Ping of Death(POD) โ€“ ๋ฉ”๋ชจ๋ฆฌ ๋ฒ„ํผ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
  • HTTP ํ”Œ๋Ÿฌ๋”ฉ โ€“ ๊ฒฐ๊ตญ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋น„์Šค์˜ ์™„์ „ํ•œ ์ข…๋ฃŒ๋ฅผ ์•ผ๊ธฐํ•˜๋Š” ์˜จ๋ผ์ธ ์„œ๋น„์Šค์˜ ์ค‘๋‹จ์„ ์ดˆ๋ž˜ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ DDoS ๊ณต๊ฒฉ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ž…๋‹ˆ๋‹ค.

  • DDoS ์œ„ํ˜‘์„ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐœ๋ฐœ ๋ฐ ๊ทธ ์ดํ›„์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์Šคํฌ๋Ÿฌ๋น™
  • ์•…์˜์ ์ธ ์‚ฌ์šฉ์ž๊ฐ€ ํ”„๋กœ๊ทธ๋žจ ์ฝ”๋“œ์— ์•ก์„ธ์Šคํ•˜์ง€ ๋ชปํ•˜๋„๋ก ๋ฐฉ๋ฌธ์ž ์‹๋ณ„ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์„ค์น˜
  • ์„œ๋ฒ„์—์„œ ํ˜ธ์ถœํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ๋Š” ํ˜ธ์ถœํ•˜์ง€ ์•Š์Œ
  • CAPTCHA ๋˜๋Š” JS ํ…Œ์ŠคํŠธ์˜ ๋„์›€์œผ๋กœ ์›น-์•ฑ ๊ณ„์ธต ๋ณด์•ˆ
  • ๋™์ผํ•œ ์†Œ์Šค์—์„œ IP์— ๋Œ€ํ•œ ์š”์ฒญ ์ˆ˜์˜ ์†๋„ ์ œํ•œ

์ฝ”๋“œ ๋‚œ๋…ํ™”

์ฝ”๋“œ ๋‚œ๋…ํ™” ๋˜๋Š” ์ตœ์†Œํ™”๋Š” ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ Uglify์™€ ๊ฐ™์€ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ๋žŒ์ด ์œก์•ˆ์œผ๋กœ ์ฝ์„ ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. React Native์˜ Java ์ฝ”๋“œ๋Š” ๋‚œ๋…ํ™”๋˜์ง€ ์•Š๋Š” ํ•œ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” DEX ํŒŒ์ผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

React Native์—๋Š” react-native-obfuscating-transformer๋กœ ์•Œ๋ ค์ง„ ๋‚œ๋…ํ™” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋‚ด์žฅ๋˜์–ด ์žˆ์–ด ์‚ฌ์šฉ์ž๊ฐ€ JavaScript ์ฝ”๋“œ์™€ ๋ชจ๋“  ๊ธฐ๋ณธ ์ฝ”๋“œ์— ๋Œ€ํ•ด ๋‚œ๋…ํ™”๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ gradle ํŒŒ์ผ์—์„œ ๋‚œ๋…ํ™”๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. buildTypes ์„น์…˜ ๋‚ด์—์„œย [app_name]/android/app/build.gradle๋กœ ์ด๋™ํ•˜์—ฌ ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

android {
ย ย ย ย // … other config
ย ย ย ย buildTypes {
ย ย ย ย ย ย ย ย release {
ย ย ย ย ย ย ย ย ย ย ย ย debuggable false
ย ย ย ย ย ย ย ย ย ย ย ย shrinkResources true
ย ย ย ย ย ย ย ย ย ย ย ย zipAlignEnabled true
ย ย ย ย ย ย ย ย ย ย ย ย minifyEnabled true
ย ย ย ย ย ย ย ย ย ย ย ย useProguard true
ย ย ย ย ย ย ย ย ย ย ย ย setProguardFiles([getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’])
ย ย ย ย ย ย ย ย }
ย ย ย ย }
ย ย ย ย // … other config
}

minifyEnabled ๋ฐ useProguard๋Š” ์ฝ”๋“œ์˜ ์ตœ์†Œํ™” ๋ฐ ๋‚œ๋…ํ™”๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ๋ฐ˜๋ฉด setProguardFiles ์ง€์‹œ๋ฌธ์€ proguard ๊ตฌ์„ฑ์˜ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. Proguard๋Š” ์ฝ”๋“œ๋ฅผ ๋ณดํ˜ธํ•˜๋Š” ๋™์‹œ์— ๋ถˆํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ์ œ๊ฑฐํ•˜๊ณ  ์ตœ์ข… ์•ฑ ํฌ๊ธฐ๋ฅผ ์ค„์ด๋Š” ์ตœ์ ํ™”๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. proguard ๊ตฌ์„ฑ์€ ์ฝ”๋“œ๋ฅผ ๋‚œ๋…ํ™”ํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ๋ชจ๋“  ์˜ˆ์™ธ๊ฐ€ ์ถ”๊ฐ€๋˜๋Š” ย (proguard-rules.pro)ํŒŒ์ผ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

React Native์˜ ์ง„๋ณดํ•œ ๋ณด์•ˆ

์•ฑ์ด ์—ฌ๋Ÿฌ ๋””๋ฐ”์ด์Šค์—์„œ ์‹คํ–‰๋˜๋ฏ€๋กœ ๋„คํŠธ์›Œํฌ ์š”์ฒญ๊ณผ ๊ด€๋ จ๋œ ๋ช‡ ๊ฐ€์ง€ ์œ„ํ˜‘์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฃจํŒ…๋˜๊ณ  ํƒˆ์˜ฅ๋œ ๊ธฐ๊ธฐ์—์„œ Intent์— ์˜ํ•ด์„œ ์•ฑ์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์€ ๋ณธ์งˆ์ ์œผ๋กœ ์•ˆ์ „ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์™„์ „ํžˆ ํ”ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฃจํŒ…๋œ ๋””๋ฐ”์ด์Šค๋Š” ๊ณต๊ฒฉ์ž๊ฐ€ OS์˜ ๋ณด์•ˆ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋ฌด๋ ฅํ™”์‹œํ‚ค๋Š”๋ฐ ๋„์›€์„ ์ฃผ๋ฉฐ, ๋ณด์•ˆ ์Šคํ† ๋ฆฌ์ง€ ๋ฐ ๋ฐ์ดํ„ฐ์— ์‰ฝ๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. JailMonkey๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋””๋ฐ”์ด์Šค์˜ ๋ฃจํŒ… ๋˜๋Š” ํƒˆ์˜ฅ ์—ฌ๋ถ€๋ฅผ ๊ฐ์ง€ํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Android ์ „์šฉ API์ธ SafetyNet์€ ๋ฃจํŒ…๋œ ๋””๋ฐ”์ด์Šค ๋ฐ ๋ถ€ํŠธ ๋กœ๋”์˜ ์ž ๊ธˆ ํ•ด์ œ๋ฅผ ๊ฐ์ง€ํ•˜๋Š”๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ณด์•ˆ ์œ„ํ˜‘, ๋””๋ฐ”์ด์Šค ๋ณ€์กฐ, ์•…์„ฑ ์•ฑ ๋ฐ ๊ฐ€์งœ ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. SafetyNet์˜ API์šฉ ๋ž˜ํผ ํ”Œ๋Ÿฌ๊ทธ์ธ์ธ react-native-google-safetynet์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ๋””๋ฐ”์ด์Šค๋ฅผ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ react-native-device-info ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์•ฑ์ด ์—๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ ์‹คํ–‰ ์ค‘์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

RASP

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

๊ฒฐ๋ก 

React Native๋Š” ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๊ณ  ํšจ์œจ์ ์ธ ์•ฑ ๊ตฌ์ถ• ํ”„๋ ˆ์ž„์›Œํฌ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ํฌ๋กœ์Šค ํ”Œ๋žซํผ ์ง€์›, ์‚ฌ์šฉ ์šฉ์ด์„ฑ, ๋น„์šฉ ๋ฐ ๋ฆฌ์†Œ์Šค ์ตœ์ ํ™”, ๋›ฐ์–ด๋‚œ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์ „์„ธ๊ณ„ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ํ›Œ๋ฅญํ•œ ์„ ํƒ์ž…๋‹ˆ๋‹ค. ์‚ฌ์ „์— ๋งŒ๋“ค์–ด์ง„ ์ปดํฌ๋„ŒํŠธ ๋ฐ ๋‚ด์žฅ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ’๋ถ€ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ฐ„๋‹จํ•œ ๊ธฐ๋Šฅ์„ ์‰ฝ๊ฒŒ ์ˆ˜ํ–‰ํ•˜๋Š”๋ฐ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ JavaScript ์ฝ”๋“œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด ์กŒ๊ธฐย  ๋•Œ๋ฌธ์— ์ด๋ฅผ ์‚ฌ์šฉํ•œ ์•ฑ์€ ๋ณด์•ˆ ์œ„ํ˜‘๊ณผ ์™ธ๋ถ€์˜ ์•…์˜์ ์ธ ์นจ์ž… ์‹œ๋„์— ๋Š˜ ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์•ฑ ์ „์ฒด์— ๊ฑธ์ณ ์œ ์‚ฌํ•œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์•„๋ฌด๋ž˜๋„ ๋ณด์•ˆ ์œ„ํ—˜๋„ ๋†’์•„์ง‘๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐœ๋ฐœ์ž๋Š” Android ๋ฐ iOS์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๋ณด์•ˆ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ React Native ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ณดํ˜ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•ฑ์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€์™€ ์‚ฌ์–‘์„ ๋ฐ”ํƒ•์œผ๋กœ ์œ„ํ˜‘ ๋ชจ๋ธ์„ ์ƒ์„ฑํ•œ ํ›„ ์•ฑ ๋ณด์•ˆ์— ๋Œ€ํ•œ ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์˜ˆ๋ฐฉ ์กฐ์น˜๋ฅผ ์ทจํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. 100% ๋ณด์•ˆ์„ ๋ณด์žฅํ•˜๋Š” ๋ฐฉํƒ„ ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ์—†์ง€๋งŒ ์ ์ ˆํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ API๋ฅผ ํ†ตํ•ฉํ•˜๋ฉด ์œ„ํ—˜ ๋ฐœ์ƒ์„ ํ™•์‹คํžˆ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ถ”๊ฐ€ ์ฝ”๋”ฉ์—†์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ณดํ˜ธํ•˜๋ ค๋ฉด ์•„๋ž˜ ๋งํฌ๋ฅผ ํด๋ฆญํ•˜์—ฌ ์•ฑ์‹ค๋ง์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ณ  ๋ฌด๋ฃŒ ํ‰๊ฐ€ํŒ์„ ์‚ฌ์šฉํ•ด ๋ณด์‹ญ์‹œ์˜ค.


appsealing signup

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

Leave a Comment

Javascript Security