โŒจ๏ธ ๊ฐค๋Ÿญ์‹œ ์ฒœ์ง€์ธ์„ ๊ทธ๋ฆฌ์›Œํ•˜๋Š” ์•„์ดํฐ ์œ ์ €๋“ค์„ ์œ„ํ•œ ํ‚ค๋ณด๋“œ

ALT: ์•„์ดํฐ ํ‚ค๋ณด๋“œ "ํ•˜๋Š˜๋•…์‚ฌ๋žŒ"์˜ ๋ชจ์Šต

์ž”๋ง ๋ง๊ณ  ์„ค์น˜ ๋งํฌ๋ถ€ํ„ฐ ์ค˜์š”

๋ฌผ๋ก ์ด์ฃ . ์•ฑ์Šคํ† ์–ด์—์„œ ์„ค์น˜ํ•ด๋ณด์„ธ์š”. GitHub ์ €์žฅ์†Œ๋„ ์žˆ๋‹ต๋‹ˆ๋‹ค.

์• ํ”Œ ์•„์ดํฐ์„ ์ฐพ๋Š” ์†Œ๋น„์ž๋“ค์ด ์ฆ๊ฐ€ํ•˜๊ณ  ์žˆ๋‹ค. ๋ˆˆ์— ๋„๋Š” ๋ถ€๋ถ„์€ ์•„์ดํฐ์„ ์ฐพ๋Š” ์ค‘์žฅ๋…„์ธต ์†Œ๋น„์ž๊ฐ€ ๋Š˜์–ด๋‚˜๊ณ  ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ๋žŒ๋“ค์€ ๊ฐค๋Ÿญ์‹œ์—์„œ ์•„์ดํฐ์œผ๋กœ ๋ชป ๋„˜์–ด์˜ค๋Š” ์ด์œ ๋กœ ํ†ตํ™”๋…น์Œ๊ณผ ์‚ผ์„ฑํŽ˜์ด๋ฅผ ๊ผฝ๋Š”๋ฐ, ๋ถ€๋ชจ๋‹˜์ด ์•„์ดํฐ์œผ๋กœ ๋ฐ”๊พธ์‹  ๋’ค์— ๋‚ด๊ฐ€ ๊ด€์ฐฐํ•œ ๋ฐ”๋Š” ์กฐ๊ธˆ ๋‹ฌ๋ž๋‹ค.

์•„๋ฌด๋„ ์†์— ๊ผฝ์ง€ ์•Š์€ ์˜ˆ์ƒ ์™ธ์˜ ๋‚œ๊ด€์€ ๋ฐ”๋กœ ํ‚ค๋ณด๋“œ์˜€๋‹ค. ๋Œ€ํ•œ๋ฏผ๊ตญ ์†Œ๋น„์ž๋“ค์€ 10ํ‚ค ํœด๋Œ€์ „ํ™” ์‹œ์ ˆ๋ถ€ํ„ฐ ๋ฌธ์ž๋ฅผ ์ž…๋ ฅํ•จ์— ์•„๋ฌด๋Ÿฐ ๋ถˆํŽธํ•จ์ด ์—†์—ˆ๋‹ค. ์„ธ์ข…์˜ ์ œ์ž ์›๋ฆฌ๋ฅผ ๋ณธ๋”ฐ ๋งŒ๋“  ์ฒœ์ง€์ธ์ด๋ผ๋Š” ๊ฐ•๋ ฅํ•œ ์ž…๋ ฅ ๋ฐฉ์‹ ๋•Œ๋ฌธ์—, ํ•˜๋‚˜์˜ ๋ฒ„ํŠผ์— ์•ŒํŒŒ๋ฒณ์ด 3๊ฐœ, 4๊ฐœ์”ฉ ๋ถ™์–ด์žˆ๋Š” ์˜๋ฏธ๊ถŒ์— ๋น„ํ•ด ์ฟผํ‹ฐ ํ‚ค๋ณด๋“œ์˜ ํ•„์š”์„ฑ์ด ํ˜„์ €ํžˆ ์ ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํƒœ์–ด๋‚ฌ์„ ๋‹น์‹œ๋ถ€ํ„ฐ ์Šค๋งˆํŠธํฐ์ด ์กด์žฌํ•˜๋˜ ์•ŒํŒŒ ์„ธ๋Œ€๊ฐ€ ์•„๋‹Œ ์ด์ƒ ์—ฌ์ „ํžˆ ์ฒœ์ง€์ธ ํ‚ค๋ณด๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๊ณ„์‹œ๋Š” ๋ถ„๋“ค์ด ๋งŽ๋‹ค.

2010๋…„ ์ฒœ์ง€์ธ์˜ ํŠนํ—ˆ๊ฐ€ ๊ฐœ๋ฐฉ๋œ ์ดํ›„ ์•„์ดํฐ์—๋„ 2013๋…„๋ถ€ํ„ฐ ์ฒœ์ง€์ธ ํ‚ค๋ณด๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์ง€๋งŒ, ์ด์ƒํ•˜๊ฒŒ๋„ ์•„์ดํฐ์€ ํ‚ค๋ณด๋“œ์˜ ๋ชจ์–‘์ด ์กฐ๊ธˆ ๋‹ฌ๋ž๋‹ค. ๊ฐ€์žฅ ๊ฒฐ์ •์ ์ธ ์ฐจ์ด์ ์€ ์ž๋ฆฌ๋„˜๊น€ ๋ฒ„ํŠผ๊ณผ ๋„์–ด์“ฐ๊ธฐ ๋ฒ„ํŠผ์ด ๋”ฐ๋กœ ์กด์žฌํ•œ๋‹ค๋Š” ์ ์ด๋‹ค.

์ž๋ฆฌ๋„˜๊น€ ๋ฒ„ํŠผ๊ณผ ๋„์–ด์“ฐ๊ธฐ ๋ฒ„ํŠผ์ด ๋”ฐ๋กœ ์กด์žฌํ•˜๋Š” ์•„์ดํฐ 10ํ‚ค ํ‚ค๋ณด๋“œ

์˜ˆ๋ฅผ ๋“ค์–ด "์˜ค ์•ˆ๋…•"์„ ์ž…๋ ฅํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”...
  • ๊ฐค๋Ÿญ์‹œ: ใ…‡ แ†ž ใ…ก โ†’ ๋„์–ด์“ฐ๊ธฐ โ†’ ใ…‡ ใ…ฃ แ†ž ใ„ด โ†’ ๋„์–ด์“ฐ๊ธฐ โ†’ ใ„ด แ†ž แ†ž ใ…ฃ ใ…‡
  • ์•„์ดํฐ: ใ…‡ แ†ž ใ…ก โ†’ ๋„์–ด์“ฐ๊ธฐ โ†’ ใ…‡ ใ…ฃ แ†ž ใ„ด โ†’ ์ž๋ฆฌ๋„˜๊น€ โ†’ ใ„ด แ†ž แ†ž ใ…ฃ ใ…‡

์ด์™€ ๊ฐ™์ด 2๊ฐ€์ง€ ๋‹ค๋ฅธ ๋ฒ„ํŠผ์ด ๋”ฐ๋กœ ์กด์žฌํ•˜๋Š” ๊ฒƒ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฒ„ํŠผ์˜ ๊ฐ ํฌ๊ธฐ๋„ ๋”์šฑ์ด ์ž‘์•„์ ธ, ์˜คํƒ€๊ฐ€ ์ง€์†์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๋“ฑ ์‚ฌ์šฉ์— ๋ถˆํŽธํ•จ์„ ํ˜ธ์†Œํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์ด ๋งŽ์•˜๋‹ค. ์ด๋Ÿฐ ์ด์œ ๋กœ ๊ฐค๋Ÿญ์‹œ ์ฒœ์ง€์ธ๊ณผ ์œ ์‚ฌํ•œ ์•„์ดํฐ ํ‚ค๋ณด๋“œ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ณ  ์‹ถ๋‹ค๋Š” ๊ฒฐ๋ก ์— ์ด๋ฅด๋ €๋‹ค.

๋ชฉํ‘œ

ํ•™์Šต ์—†์ด ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์•„์ดํฐ์šฉ ์ฒœ์ง€์ธ ํ‚ค๋ณด๋“œ๋ฅผ ๋งŒ๋“ค์–ด ๋ณด์ž!

๊ฟ€ํŒ

์ด ํ”„๋กœ์ ํŠธ์˜ ์—ฐ๊ตฌ ๊ธฐ๋ก๋„ ๊ณต๊ฐœ๋˜์–ด ์žˆ๋‹ค.

๐Ÿ“œ ํŠนํ—ˆ๊ถŒ ๋ฐ ๋ฒ•์  ๊ถŒ๋ฆฌ

์šฐ์„  ํŠนํ—ˆ๊ถŒ๊ณผ ๋ฒ•์  ๊ถŒ๋ฆฌ์— ์•„๋ฌด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†๋Š”์ง€๋ฅผ ํ™•์ธํ–ˆ๋‹ค. ์กฐ์‚ฌํ•œ ๊ฒฐ๊ณผ, ์กฐ๊ด€ํ˜„ ํŠนํ—ˆ๊ถŒ ๋ณด์œ ์ž๋‹˜๊ป˜์„œ ํŠนํ—ˆ๊ถŒ์„ ์ •๋ถ€์— ๊ธฐ์ฆํ•˜์…” ๊ตญ๊ฐ€ ํ‘œ์ค€์œผ๋กœ ์ฑ„ํƒ๋œ ์ดํ›„, ํ•œ๊ธ€ ์žํŒ์— ๋Œ€ํ•œ ํŠนํ—ˆ๊ถŒ ์‚ฌ์šฉ๊ถŒ์ด ๋ฌด์ƒ์œผ๋กœ ํ—ˆ์šฉ๋˜์—ˆ๋‹ค. ์ด์— ์•„๋ฌด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†์Œ์„ ํ™•์ธํ•œ ํ›„ ๊ฐœ๋ฐœ์— ์ฐฉ์ˆ˜ํ–ˆ๋‹ค.

๐Ÿ›  ๊ธฐ์ˆ  ์ •ํ•˜๊ธฐ

์•„์ดํฐ ์žํŒ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์• ํ”Œ์˜ ์ปค์Šคํ…€ ํ‚ค๋ณด๋“œ ๋งŒ๋“ค๊ธฐ ๋ฌธ์„œ๋ฅผ ์ •๋…ํ–ˆ๋‹ค. ํ™•์ธ ๊ฒฐ๊ณผ ์ผ๋ฐ˜์ ์ธ ์•„์ดํฐ ์•ฑ์„ ์ œ์ž‘ํ•˜๋Š” ๋‚œ์ด๋„์™€ ๋น„์Šทํ•ด๋ณด์˜€๋‹ค. ์ผ๋‹จ ViewController ๋‚ด์— ๋ฒ„ํŠผ๊ณผ ๋กœ์ง์„ ๋•Œ๋ ค๋ฐ•์•„ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์€ ์‰ฌ์›Œ๋ณด์˜€์œผ๋‚˜, SwiftUI๋ฅผ ์ด์šฉํ•œ iOS ๊ฐœ๋ฐœ์„ ํ•œ ์ ์ด ์—†์–ด SwiftUI๋กœ ๊ฐœ๋ฐœํ•ด๋ณด๊ณ  ์‹ถ์—ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” ์ƒˆ๋กœ ๋‚˜์˜จ SwiftUI Grid ๊ธฐ๋Šฅ์„ ์“ฐ๋ฉด ๊น”๋”ํ•˜๊ฒŒ ๋ฒ„ํŠผ์„ ๋ฐฐ์—ดํ•  ์ˆ˜ ์žˆ์„ ๋“ฏํ–ˆ๋Š”๋ฐ, ์ด๋Š” ์‚ฌ์ง„ ์•ฑ์ฒ˜๋Ÿผ ์ˆ˜๋งŽ์€ ์—˜๋ฆฌ๋จผํŠธ๋“ค์„ ํ™”๋ฉด์— ๋ฐฐ์—ดํ•˜๋Š” ๊ฒƒ์— ๋” ์ตœ์ ํ™”๋˜์–ด์žˆ๊ณ , ๋‚˜์˜ ๊ฒฝ์šฐ์ฒ˜๋Ÿผ ๋ฒ„ํŠผ์˜ ๊ฐœ์ˆ˜๊ฐ€ ์ •ํ•ด์ ธ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” (์›น์—์„œ์˜ display: flex์™€ ์œ ์‚ฌํ•œ) HStack๊ณผ VStack์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

์•„์ดํฐ ์จ๋“œํŒŒํ‹ฐ ํ‚ค๋ณด๋“œ๋Š” ์ต์Šคํ…์…˜์ด๋ผ๋Š” ๋…ํŠนํ•œ ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•ด ์ œ์ž‘ํ•œ๋‹ค. iOS ์•ฑ ๋ณธ์ฒด๊ฐ€ ์•„๋‹ˆ๋ฉด ์ „๋ถ€ ์ต์Šคํ…์…˜์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. ์ปค์Šคํ…€ ํ‚ค๋ณด๋“œ๋„ ์ต์Šคํ…์…˜์ด๊ณ , iOS ์œ„์ ฏ๋„ ์ต์Šคํ…์…˜์ด๊ณ , ์• ํ”Œ ์›Œ์น˜ ์•ฑ์—๋„ ์ต์Šคํ…์…˜์ด ํƒ‘์žฌ๋œ๋‹ค. Ray Wenderlich์˜ ๋ฌธ์„œ๋ฅผ ์ฝ์œผ๋ฉฐ ๊ฐ„๋‹จํ•œ ๋ฐ๋ชจ๋“ค์„ ์ œ์ž‘ํ•˜๋ฉฐ ํ‚ค๋ณด๋“œ ์ต์Šคํ…์…˜์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ๋†’์˜€๋‹ค.

ALT: ใ…‡ ๊ทผ์ฒ˜ ๋ฐฐ๊ฒฝ์— ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ์ด ์žˆ๋Š” ํ‚ค๋ณด๋“œ ์ž…๋ ฅ ๋ชจ์Šต

ALT: ใ…‡ ๊ทผ์ฒ˜ ๋ฐฐ๊ฒฝ์— ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ์ด ์žˆ๋Š” ํ‚ค๋ณด๋“œ ์ž…๋ ฅ ๋ชจ์Šต

ALT: ใ…‡ ๊ทผ์ฒ˜ ๋ฐฐ๊ฒฝ์— ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ์ด ์žˆ๋Š” ํ‚ค๋ณด๋“œ ์ž…๋ ฅ ๋ชจ์Šต

๋ช‡ ๊ฐ€์ง€ ์ดˆ๊ธฐ ๋ฒ„์ „๋“ค

๊ฐ€์šด๋ฐ ์ด๋ฏธ์ง€์˜ "ใ…‡" ๊ทผ์ฒ˜์˜ ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ์€ iOS ๊ธฐ๊ธฐ์˜ NSRange์™€ setMarkedText๋ผ๋Š” ๊ธฐ๋Šฅ์ด์—ˆ๋‹ค. ์ž…๋ ฅ ์ค‘์ธ ๊ธ€์ž์— ์˜์—ญ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์„œ ์ž…๋ ฅ์„ ๋„์™€์ฃผ๋Š” ๊ธฐ๋Šฅ์ด์—ˆ๋Š”๋ฐ, ์ค‘๊ตญ์–ด์˜ ํ•œ์–ด๋ณ‘์Œ(Pinyin)์ฒ˜๋Ÿผ ๋ฌธ์ž ์ž…๋ ฅ ์ง์ „์— ์กฐํ•ฉ์šฉ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์œผ๋กœ ์ฒœ์ง€์ธ์šฉ์œผ๋กœ ์ ์ ˆํ•˜์ง€ ์•Š๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค. ๋˜ํ•˜๋‚˜ ํฅ๋ฏธ๋กœ์šด ์ ์œผ๋กœ ์•„์ดํฐ ๊ธฐ๋ณธ ์žํŒ์˜ ์ƒ‰์ƒ๋“ค์€ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณต๋˜๋Š” ์–ด๋–ค systemColor์™€๋„ ๋‹ฌ๋ž๋‹ค. ์ƒ‰๊น”์„ Color Meter๋กœ ๋ฝ‘์•„ ํ•˜๋‚˜ํ•˜๋‚˜ ์ž…๋ ฅํ–ˆ๋‹ค.

๐Ÿ˜ถโ€๐ŸŒซ๏ธ ๊ทธ๋Ÿฐ๋ฐ ์ฒœ์ง€์ธ์€ ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค์ง€

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

  • ์•Š์„ ์ž…๋ ฅํ•˜๊ธฐ ์œ„ํ•ด ์•ˆใ……์ด ์ž…๋ ฅ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ์— ใ……ใ…Ž ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์•Š์ด ๋˜์–ด์•ผ ํ•œ๋‹ค. ์ด์ „ ๊ธ€์ž์™€ ์žฌ๊ฒฐํ•ฉ์ด ๊ฐ€๋Šฅํ•œ์ง€ ํŒ๋‹จํ•ด์•ผ ํ•œ๋‹ค.
  • ์•‰์—์„œ ใ…ก๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์•ˆ์ฆˆ๊ฐ€ ๋˜์–ด์•ผ ํ•œ๋‹ค. ๋งˆ์ง€๋ง‰ ์ข…์„ฑ ํ•˜๋‚˜๊ฐ€ ์ถ”์ถœ ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. ์ด์ „ ๊ธ€์ž์—์„œ ์ถ”์ถœ์ด ๊ฐ€๋Šฅํ•œ์ง€ ํŒ๋‹จํ•ด์•ผ ํ•œ๋‹ค.
  • ๊นš์—์„œ ใ…‚ใ… ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ๊น”ใ…ƒ์ด ๋˜์–ด์•ผ ํ•œ๋‹ค. ์ฆ‰ ์ข…์„ฑ ์ถ”์ถœ + ๋œ์†Œ๋ฆฌ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•œ์ง€ ํŒ๋‹จํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ฐŒ์—์„œ ใ……ใ…Ž ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ๊ฐ์ด ๋˜์–ด์•ผ ํ•œ๋‹ค. ์ฆ‰, ๋‹จ์ˆœํ•˜๊ฒŒ ใ……, ใ…Ž, ใ…†๋ฅผ ์ƒํ˜ธ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ใ„ฝ ๋“ฑ์˜ ๊ฒน๋ฐ›์นจ์˜ ๋ณ€ํ™˜๋„ ํŒ๋‹จํ•ด์•ผ ํ•œ๋‹ค.

์ด ์™ธ์—๋„ ์ˆ˜๋งŽ์€ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ์กฐํ•ฉํ˜• ํ•œ๊ธ€์„ ๋‚˜์—ดํ•ด๋‘๊ณ  ํ•˜๋‚˜ํ•˜๋‚˜ ๊ณ„์‚ฐ์„ ํ•œ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„, ์œ„ ๊ฒฝ์šฐ์˜ ์ˆ˜๋ฅผ ๋ชจ๋‘ ๊ณ ๋ คํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ์–ด๋ ต๋‹ค. ์œ ํ•œ์ƒํƒœ๊ธฐ๊ณ„๋กœ ์ œ์ž‘ํ•  ๊ฒฝ์šฐ ๋‘๋ฒŒ์‹๋ณด๋‹ค ํ›จ์”ฌ ๋งŽ์€ ์•ฝ 20๊ฐœ ์ •๋„์˜ ๋ฐ์ดํ„ฐ ์ €์žฅ ์Šคํƒ๊ณผ ์ˆ˜์‹ญ ๊ฐ€์ง€์˜ ์ƒํƒœ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค. (์ •ํ™•ํžˆ ๊ณ„์‚ฐํ•˜์ง€๋Š” ์•Š์•˜์œผ๋‹ˆ ๋” ๋‹จ์ˆœํ•œ ๊ตฌํ˜„ ๋กœ์ง์ด ์กด์žฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ํ˜น์‹œ๋ผ๋„ ์ด ๋ฐฉ์‹๋Œ€๋กœ ์ง„์งœ ๋งŒ๋“ค์–ด๋ณด๊ณ  ์‹ถ์€ ์‚ฌ๋žŒ์ด ์žˆ๋‹ค๋ฉด ์ด ํŠนํ—ˆ์˜ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ์ฐธ๊ณ ํ•˜์ž.) ์ธํ„ฐ๋„ท ์ƒ์— ๋ช‡ ๊ฐ€์ง€ ๊ตฌํ˜„ ๋กœ์ง๋“ค์„ ๋ฐœ๊ฒฌํ•˜๊ธฐ๋Š” ํ–ˆ์ง€๋งŒ, ์ „๋ถ€ ๊ธธ๊ณ  ๋ณต์žกํ•ด Swift๋กœ์˜ ๋ฒˆ์—ญ์„ ์ฐจ์น˜ํ•˜๊ณ  ์ฝ”๋“œ ์ž์ฒด๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ๋„ ํ•œ์„ธ์›”์ด ๊ฑธ๋ฆด ๋“ฏ ํ–ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค ๋ฌธ๋“ ์ด๋Ÿฐ ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

์ผ€์ด์Šค๊ฐ€ ๋„ˆ๋ฌด ๋งŽ๊ณ  ๋ณต์žกํ•˜๋ฉด

๊ทธ๋ƒฅ ์ „๋ถ€ ํ•˜๋“œ์ฝ”๋”ฉํ•˜๋ฉด ๋˜์ž–์•„?

์ƒ๊ฐํ•ด๋ณด๋ฉด ๊ฐ™์€ ๋ฒ„ํŠผ ์กฐํ•ฉ์„ ์ž…๋ ฅํ•˜๋ฉด ๊ฐ™์€ ๊ธ€์ž๋กœ ์ฐํ˜€์•ผํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ ํ‚ค๋ณด๋“œ์ด๋ฆฌ๋ผ. ๊ทธ๋ƒฅ ๋ชจ๋“  ๊ฒฝ์šฐ์˜ ์ˆ˜๋ฅผ ์ƒ์„ฑํ•ด์„œ ํ•˜๋‚˜์˜ ๊ฑฐ๋Œ€ํ•œ JSON ํŒŒ์ผ์— ๋„ฃ์–ด๋ฒ„๋ฆฌ๋ฉด ์–ด๋–จ๊นŒ! ๋‹จ์ˆœํ•˜๊ฒŒ ์…ˆ์„ ํ•ด๋ณด์•„๋„ ํ•œ๊ธ€์€ ์•ฝ 11,000์ž ๋ฐ–์— ์•ˆ ๋˜๊ณ , ์•ž๋’ค ๊ธ€์ž๊นŒ์ง€ ๊ฐ™์ด ๊ณ ๋ คํ•˜๋Š” ์ผ€์ด์Šค๋งŒ ๊ณ ๋ คํ•œ๋‹ค๊ณ  ํ•ด๋„ ์กฐํ•ฉ์€ ๋งŽ์•„์•ผ 10๋งŒ ๋‹จ์œ„๋ฅผ ๋„˜์–ด๊ฐ€์ง€ ์•Š์„ ๊ฒƒ์œผ๋กœ ํŒ๋‹จํ–ˆ๋‹ค. JSON์˜ ํฌ๊ธฐ๋Š” 2MB ์•ˆํŒŽ์—์„œ ๋„˜์–ด๊ฐ€์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค.

๊ณผ๊ฑฐ์ฒ˜๋Ÿผ ์ž„๋ฒ ๋””๋“œ ํ•˜๋“œ์›จ์–ด์—์„œ KB ๋‹จ์œ„๋กœ ๊ณจํ”„๋ฅผ ์น˜๋ฉฐ ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”๋ฅผ ํ•ด์•ผํ•˜๋Š” ์‹œ๋Œ€๊ฐ€ ์•„๋‹ˆ๋‹ค. ํ•œ๊ธ€์ด ์ธ๋ฅ˜์™€ ํ•จ๊ป˜ํ•˜๋Š” ์ด์ƒ ๋ถ„๋ช… ๋ฏธ๋ž˜์˜ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ฒœ์ง€์ธ์„ ๋‹ค์‹œ ์ œ์ž‘ํ•  ์ผ์ด ์žˆ์„ ๊ฒƒ์ด๊ณ , ๊ทธ๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์˜จ์ „ํ•œ ์ฒœ์ง€์ธ ์ง€๋„๋ฅผ ๋งŒ๋“ค ๊ฐ€์น˜๊ฐ€ ์ถฉ๋ถ„ํžˆ ์กด์žฌํ•œ๋‹ค.

๐Ÿ–จ๏ธ ํ™œ์ž: ์„ธ์ƒ์—์„œ ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์ฒœ์ง€์ธ ๊ตฌํ˜„์ฒด

๊ทธ๋กœ ์ธํ•ด์„œ ํ™œ์ž๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ์ฒœ์ง€์ธ์˜ ๋ชจ๋“  ์ƒํƒœ์™€ ์กฐํ•ฉ์„ ๋‹ด์•„๋†“์€ ํ•œ๊ธ€ ์ง€๋„ ๐Ÿ—บ๏ธ ์ด๋‹ค. ์ด 5๋งŒ ์—ฌ๊ฐœ์˜ ์ž…๋ ฅ ์ผ€์ด์Šค๊ฐ€ ์กด์žฌํ•˜๋ฉฐ, ์••์ถ•๋œ JSON์€ 500KB ์ •๋„ ํฌ๊ธฐ์ด๋‹ค.

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

const type = (์ด์ „: string, ํ™œ์ž: hwalja, ํ‚ค: string, ์ˆ˜์ •์ค‘: boolean) => {
  const ๋งˆ์ง€๋ง‰ํ•œ๊ธ€์ž = ์ด์ „.slice(-1)
  const ๋งˆ์ง€๋ง‰๋‘๊ธ€์ž = ์ด์ „.slice(-2)
  if (์ˆ˜์ •์ค‘ && ๋งˆ์ง€๋ง‰๋‘๊ธ€์ž in ํ™œ์ž[ํ‚ค]) return ์ด์ „.slice(0, -2) + ํ™œ์ž[ํ‚ค][๋งˆ์ง€๋ง‰๋‘๊ธ€์ž]
  if (์ˆ˜์ •์ค‘ && ๋งˆ์ง€๋ง‰ํ•œ๊ธ€์ž in ํ™œ์ž[ํ‚ค]) return ์ด์ „.slice(0, -1) + ํ™œ์ž[ํ‚ค][๋งˆ์ง€๋ง‰ํ•œ๊ธ€์ž]
  return ์ด์ „ + ํ™œ์ž[ํ‚ค]['']
}

๋‹จ 5์ค„ ๋งŒ์œผ๋กœ ์ฒœ์ง€์ธ ๋กœ์ง ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ˆ ๋‚˜๋Š” ๊ฐํžˆ ์ด๊ฒƒ์„ ์ „ ์„ธ๊ณ„์—์„œ ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์ฒœ์ง€์ธ ๊ตฌํ˜„์ฒด๋ผ๊ณ  ์นญํ•˜๊ฒ ๋‹ค.

์ „์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด ๊ถ๊ธˆํ•œ ์‚ฌ๋žŒ๋“ค์ด ์žˆ์„ํ…๋ฐ, ์ž…๋ ฅ ๊ฐ€๋Šฅํ•œ 11,000์—ฌ ์ž์˜ ํ•œ๊ธ€ ๊ธ€์ž๋“ค์„ ์ข…์ฐฉ์ ์œผ๋กœ ์‚ผ์•„ ๊ทธ ๊ธ€์ž๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ˆŒ๋Ÿฌ์•ผํ–ˆ์„ ๊ธ€์‡ ๋Š” ๋ฌด์—‡์ธ์ง€, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๊ธ€์‡ ๋ฅผ ๋ˆ„๋ฅด๊ธฐ ์ „ ์ƒํƒœ๋Š” ๋ฌด์—‡์ธ์ง€ ์—ญ์‚ฐํ–ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์—ญ์ด ์žˆ๋‹ค๋ฉด ์ด์ „ ์ƒํƒœ๋Š” ์—ฌ์ด๊ณ  ใ„ฑ์„ ๋ˆŒ๋Ÿฌ์„œ ์—ญ์— ๋„๋‹ฌํ–ˆ๊ฒ ๊ตฐํ•˜๋Š” ์‹์œผ๋กœ ์—ญ์œผ๋กœ ๊ณ„์‚ฐํ•œ ๊ฒƒ์ด๋‹ค. ๋ฌผ๋ก  ์ด์— ๋”ํ•ด ์—ฌ๋Ÿฌ ์—ฃ์ง€ ์ผ€์ด์Šค๋“ค์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ–ˆ๋‹ค. 4๋…„ ์ „ ์กฐ์„ฑํ˜„์ด ๋‚˜๋ฅผ ๋งŽ์ด ๋„์™€์ฃผ์—ˆ๋‹ค.

์ง์ ‘ ํ•ด๋ณด์ž!

๋‹ค์Œ ์ฐฝ์€ ํ™œ์ž๋ฅผ ์ด์šฉํ•ด ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•ด๋ณธ ์ฒœ์ง€์ธ ์ž…๋ ฅ ์‹œ์—ฐ์ด๋‹ค.

ํ™œ์ž๋Š” ๋ชจ๋“  ํ”Œ๋žซํผ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ณต๊ฐœํ–ˆ๋‹ค.
์œ„ ๋ฐ๋ชจ๋กœ ์ง์ ‘ ํ™œ์ž๋ฅผ ์ž…๋ ฅํ•ด๋ณด์ž!

ํ™œ์ž๋Š”

๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๊ตฌํ˜„์ฒด์ด์ง€ ๊ฐ€์žฅ ๊ฐ€๋ฒผ์šด ๊ตฌํ˜„์ฒด๋Š” ์•„๋‹ˆ๋ผ๋Š” ์ ์„ ๋ช…์‹ฌํ•˜์ž.

์กฐํ•ฉํ˜• ํ•œ๊ธ€๋กœ ๋ฌธ์ž์—ด ์ •๊ทœํ™”๋ฅผ ํ•˜๋ฉด ์ข€ ๋” ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ์ค„์ง€ ์•Š๋‚˜์š”?

ํ™œ์ž ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•ด ์ด์„ฑ๊ด‘ ๋‹˜๊ป˜์„œ NFD๋กœ ๋ฌธ์ž์—ด ์ •๊ทœํ™”๋ฅผ ํ•ด์„œ ์ดˆ์ค‘์ข…์„ฑ์„ ๋–ผ์–ด๋†“๊ณ  ๋งŒ๋“ค๋ฉด ์ข€ ๋” ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ์ค„์ง€ ์•Š์„๊นŒ์— ๋Œ€ํ•œ ์ง€์ ์„ ํ•ด์ฃผ์…จ๋‹ค. ๋‚˜๋Š” ์™„์„ฑํ˜• ํ•œ๊ธ€๋งŒ ๋†“๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ, ๋ง์”€ํ•˜์‹ ๋Œ€๋กœ ์กฐํ•ฉํ˜•์œผ๋กœ ์ œ์ž‘ ํ›„ ์ •๊ทœํ™”๋ฅผ ๊ฑฐ์น˜๋ฉด ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ํ™•์‹คํžˆ ๋งŽ์ด ์ค€๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•ˆ แ„‚แ†žแ†žใ…ฃใ…‡ ๊ฐ™์ด ํ’€์–ด๋‘๊ณ  แ†žแ†žใ…ฃ ๋ถ€๋ถ„๋งŒ ใ…•๋กœ ์กฐํ•ฉํ•œ ๋’ค, ใ„ดใ…•ใ…‡์„ ์ •๊ทœํ™” ๊ณผ์ •์„ ํ†ตํ•ด ๋…•์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

ํ™œ์ž ํ”„๋กœ์ ํŠธ์˜ ๊ฒฝ์šฐ ํ˜„ ์ ‘๊ทผ์„ ์œ ์ง€ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. ํ™œ์ž๋Š” ๊ฐ€์žฅ ์‰ฝ๊ณ  ๋‹จ์ˆœํ•œ ์ฒœ์ง€์ธ ๊ตฌํ˜„์ฒด๋ฅผ ์ง€ํ–ฅํ•˜๋Š” ๋งŒํผ ํ˜„์žฌ์˜ ์ ‘๊ทผ์ด substring + replace ๋งŒ์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋งŒ์•ฝ NFD์™€ ์ •๊ทœํ™”์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผํ•œ๋‹ค๋ฉด, ๋น„๋ก ํ™œ์ž ํ”„๋กœ์ ํŠธ ์ž์ฒด๋Š” ๊ฐ€๋ฒผ์›Œ์ง€๊ฒ ์ง€๋งŒ, ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋ฐœ์ž ์ธก์—์„œ NFD์™€ ์ •๊ทœํ™”์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ํ•™์Šต ๋ฐ ๊ตฌํ˜„์ด ํ•„์š”ํ•˜๋‹ค. ์˜คํ† ๋งˆํƒ€๋ฅผ ์ด์šฉํ•œ ํ•™์Šต ๊ณก์„ ์— ๋ถˆํŽธํ•จ์„ ๋А๊ปด ๋ชจ๋“  ์ •๋ณด๋ฅผ ํ•˜๋“œ์ฝ”๋”ฉํ•ด ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ํ˜•ํƒœ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ํ•œ ํ™œ์ž ํ”„๋กœ์ ํŠธ์˜ ๋ณธ ๋ชฉ์ ์— ์–ด๊ธ‹๋‚œ๋‹ค. ๋”๋ถˆ์–ด ์ด๋ฏธ ์••์ถ•๋œ ๋ฒ„์ „์€ 500KB ์ˆ˜์ค€์ด๊ธฐ์— ์ž…๋ ฅ ์—”์ง„์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ์— ๋ถ€๋‹ด์ด ๋˜๋Š” ํฌ๊ธฐ๊ฐ€ ์•„๋‹ˆ๋‹ค.

๐Ÿค– ์ž๋™์™„์„ฑ ํ‚ค๋ณด๋“œ ๋งŒ๋“ค๊ธฐ

์ฒœ์ง€์ธ์„ ์“ฐ๋Š” ์‚ฌ๋žŒ๋“ค์ด ๋น ๋ฅธ ์†๋„๋กœ ํƒ€์ž๋ฅผ ์น  ์ˆ˜ ์žˆ๋Š” ์ด์œ ๋Š” ๋ฐ”๋กœ ์ž๋™ ์™„์„ฑ ํ…์ŠคํŠธ (๊ฐœ๋ฐœ ๋ช…์นญ: Apple QuickType)์„ ์ ๊ทน์ ์œผ๋กœ ํ™œ์šฉํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ์ด ์ž๋™ ์™„์„ฑ ํ…์ŠคํŠธ๋“ค์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๋Š” ํŒจํ„ด์„ ์ง€์†์ ์œผ๋กœ ํ•™์Šตํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์„ ๋•๋Š”๋‹ค.

๋‹คํ–‰ํžˆ๋„ ์• ํ”Œ UIKit์—๋Š” Core ML๊ณผ Neural Engine์„ ์ง์ ‘ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ ๋„ ๋ฌธ๋งฅ ์ž๋™ ์™„์„ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” UITextChecker๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ํ•œ๊ตญ์–ด๋„ ๋ฌผ๋ก  ์ง€์›ํ•˜๋ฉฐ, learnWord()์™€ unlearnWord()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์„ ํ•™์Šตํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋‹ค.



let uiTextChecker = UITextChecker()
let input = "ํ–‰๋ณตํ•˜"
let guesses = uiTextChecker.completions(
    forPartialWordRange: NSRange(location: 0, length: input.count),
    in: input,
    language: "ko-KR"
)

/*
[
  "ํ–‰๋ณตํ•œ", "ํ–‰๋ณตํ•ฉ๋‹ˆ๋‹ค", "ํ–‰๋ณตํ•˜๊ฒŒ", "ํ–‰๋ณตํ• ", "ํ–‰๋ณตํ•˜๋‹ค", "ํ–‰๋ณตํ•˜๊ณ ", "ํ–‰๋ณตํ•˜์ง€",
  "ํ–‰๋ณตํ•˜๋‹ค๊ณ ", "ํ–‰๋ณตํ•˜๋‹ค๋Š”", "ํ–‰๋ณตํ•˜๊ธฐ", "ํ–‰๋ณตํ•˜๋ฉด", "ํ–‰๋ณตํ• ๊นŒ", "ํ–‰๋ณตํ•˜๊ธธ",
  "ํ–‰๋ณตํ•จ์„", "ํ–‰๋ณตํ•˜๊ธฐ๋ฅผ", "ํ–‰๋ณตํ•จ", "ํ–‰๋ณตํ•˜๋‹ˆ", "ํ–‰๋ณตํ•œํ…Œ", "ํ–‰๋ณตํ•˜์ž", "ํ–‰๋ณตํ•˜๋„ค"
]
*/

์ด ๊ธฐ๋Šฅ์„ ์ด์šฉํ•ด ์ž๋™์™„์„ฑ ๊ธฐ๋Šฅ์„ ์™„์„ฑํ–ˆ๋‹ค. ๊ฐ€๋” ๋ฌธ๋งฅ์ด ์–ด์ƒ‰ํ•˜๊ฑฐ๋‚˜ ์•„๋ฌด๋Ÿฐ ์ถ”์ฒœ์„ ํ•ด์ฃผ์ง€ ์•Š๊ฑฐ๋‚˜ ํ•˜๋Š” ๋ฒ„๊ทธ๊ฐ€ ์กด์žฌํ•˜์ง€๋งŒ, ์ตœ์†Œ ๊ธฐ๋Šฅ ์ œํ’ˆ์„ ์œ„ํ•ด์„œ๋Š” ํ›Œ๋ฅญํ•˜๊ฒŒ ๋™์ž‘ํ•œ๋‹ค!

2023๋…„์—๋„ ํ–‰๋ณตํ•˜์„ธ์š” ๐Ÿ’™

โŒจ๏ธ ํ‚ค๋ณด๋“œ ๊ธฐ๋Šฅ ๊ณ ๋„ํ™”

์ฒœ์ง€์ธ์€ ๋‹จ์ถ•ํ‚ค์— ๊ธฐ์›์„ ๋‘๊ณ  ์žˆ๋Š” ๋งŒํผ ๊ด€๋ จ๋œ ๊ณ ๋„ ๊ธฐ๋Šฅ๋“ค์ด ์žˆ๋‹ค. ๋ฐฑ์ŠคํŽ˜์ด์Šค ํ‚ค๋ฅผ ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๋ฉด ๋—„ ๋•Œ๊นŒ์ง€ ๊ธ€์ด ์‚ญ์ œ๋˜๋Š” ๊ธฐ๋Šฅ์ด๋‚˜, ์ž…๋ ฅ ํ‚ค๋ฅผ ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๋ฉด ๊ทธ ๋ฒ„ํŠผ์— ๋Œ€์‘ํ•˜๋Š” ์ˆซ์ž๊ฐ€ ์ž…๋ ฅ๋˜๋Š” ๊ธฐ๋Šฅ ๋“ค์ด ์žˆ๋‹ค. ์ด ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Swift์˜ Closure๋ฅผ ํ™œ์šฉํ•ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ‚ค๋ณด๋“œ ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™•์žฅํ–ˆ๋‹ค.

struct KeyboardButton: View {
  var onPress: () -> Void
  var onLongPress: () -> Void
  var onLongPressFinished: () -> Void
  var body: some View {
    Button(action: {})
      .simultaneousGesture(
        DragGesture(minimumDistance: 0) // <-- A
          .onChanged { _ in
            // ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๊ฑฐ๋‚˜ ๋“œ๋ž˜๊ทธํ–ˆ์„ ๋•Œ ๊ตฌ๋™๋  ์ฝ”๋“œ
            onLongPress()
          }
          .onEnded { _ in
            // ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๋Š” ๋™์ž‘ ๋˜๋Š” ๋“œ๋ž˜๊ทธ ๋™์ž‘์ด ๋๋‚  ๋•Œ
            onLongPressFinished()
          }
      )
      .highPriorityGesture(
        TapGesture()
          .onEnded { _ in
            // ํ„ฐ์น˜ํ–ˆ์„ ๋•Œ ๊ตฌ๋™๋  ์ฝ”๋“œ
            onPress()
          }
      )
  }
}

์„ค๋ช…์„ ์œ„ํ•ด ๊ฐ„์†Œํ™”๋œ ์ฝ”๋“œ์ด๋‹ค. KeyboardButton.swift

A๋กœ ํ‘œํ˜„๋œ ๋ถ€๋ถ„์„ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ฐœํ•œ ๋ฐฉ๋ฒ•์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‹ค์Œ ๋‘ ๋งˆ๋ฆฌ ํ† ๋ผ๋ฅผ ํ•œ ์ฝ”๋“œ๋กœ ์žก์„ ์ˆ˜ ์žˆ๋‹ค.

  • ํ•œ๊ธ€ ๋ฒ„ํŠผ์„ ์Šค์™€์ดํ”„(flick)ํ•ด ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ
  • ํ•œ๊ธ€ ๋ฒ„ํŠผ์„ ๊ธธ๊ฒŒ ๋ˆŒ๋Ÿฌ ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ

DragGesture์˜ minimumDistance๊ฐ€ 0์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋‹ค๋ฉด ๋กฑํ”„๋ ˆ์Šค๋„ ๋™์‹œ์— ์ธ์‹ํ•˜์—ฌ highPriorityGesture๋ฅผ ์ทจ์†Œํ•˜๊ณ  DragGesture์— ํ•ด๋‹นํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•œ๋‹ค๋Š” ํŠน์ง•์„ ์ด์šฉํ•œ ๊ฒƒ์ด๋‹ค.

๋”๋ถˆ์–ด iOS13๋ถ€ํ„ฐ ์†Œ๊ฐœ๋œ Combine ๋ฌธ๋ฒ•์„ ์‹œ๋ฒ”์ ์œผ๋กœ ์‚ฌ์šฉํ•ด๋ณด์•˜๋‹ค. Combine ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ๋น„๋™๊ธฐ์  ๋™์ž‘์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ Declarative Swift API์ด๋‹ค. ์ด๋ฅผ ์ด์šฉํ•ด ํƒ€์ด๋จธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  "๋กฑํ”„๋ ˆ์Šค ๋ฐฑ์ŠคํŽ˜์ด์Šค" ๋™์ž‘์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

struct DeleteButton: View {
  @State var timer: AnyCancellable?
  var body: some View {
    KeyboardButton(systemName: "delete.left.fill", primary: false, action: {
      // ํƒญํ–ˆ์„ ๋•Œ๋Š” ๊ธฐ๋ณธ ์‚ญ์ œ ๋™์ž‘์„ ์‹คํ–‰ํ•œ๋‹ค.
      options.deleteAction()
    },
    onLongPress: {
      // ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๊ณ  ์žˆ์„ ๊ฒฝ์šฐ 0.1์ดˆ๋งˆ๋‹ค ์‹คํ–‰๋˜๋Š” ํƒ€์ด๋จธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
      timer = Timer.publish(every: 0.1, on: .main, in: .common)
        .autoconnect()
        .sink { _ in
          // ๋ˆ„๋ฅด๊ณ  ์žˆ๋Š” ๋™์•ˆ 0.1์ดˆ์— ํ•œ ๋ฒˆ์”ฉ ๊ธ€์ž ํ•˜๋‚˜๋ฅผ ์ง€์šด๋‹ค.
          options.deleteAction()
        }
    },
    onLongPressFinished: {
      // ์†์„ ๋–ผ๋ฉด ํƒ€์ด๋จธ๊ฐ€ ์ทจ์†Œ๋˜๋ฉฐ ๋ฌดํ•œ ๋ฐ˜๋ณต ์‚ญ์ œ ๋™์ž‘์ด ํ•ด์ œ๋œ๋‹ค.
      timer?.cancel()
    })
  }
}

์„ค๋ช…์„ ์œ„ํ•ด ๊ฐ„์†Œํ™”๋œ ์ฝ”๋“œ์ด๋‹ค. HangulView.swift

์ด๋ ‡๊ฒŒ ์กฐํ•ฉ๋œ ํ•˜๋‚˜์˜ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๊ฑฐ๋‚˜ ๋“œ๋ž˜๊ทธ๋ฅผ ์ด์šฉํ•ด ํŠน์ˆ˜ํ•œ ๋™์ž‘์„ ์‹คํ–‰ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๐Ÿฆพ ์ ‘๊ทผ์„ฑ๊ณผ ์‚ฌ์šฉ์„ฑ

์œ ์šฉํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•œ ์ ‘๊ทผ์„ฑ ๊ธฐ๋Šฅ์„ ๋ช‡ ๊ฐ€์ง€ ์ถ”๊ฐ€ํ–ˆ๋‹ค. ์šฐ์„  ๊ฐ€์žฅ ๋จผ์ € ์‚ฌ์šฉ์ž๊ฐ€ "๋ณผ๋“œ์ฒด ํ…์ŠคํŠธ" ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•œ ๊ฒฝ์šฐ ๊ธ€์ž ๋‘๊ป˜๋ฅผ ๋‘๊ป๊ฒŒ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

let fontWeight: UIAccessibility.isBoldTextEnabled ? .bold : .regular

๋ณผ๋“œ์ฒด ํ…์ŠคํŠธ๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ

๋ณผ๋“œ์ฒด ํ…์ŠคํŠธ๋ฅผ ํ™œ์„ฑํ™”ํ•œ ๊ฒฝ์šฐ

๋˜ ๋‚˜๋ฆ„ ํฐ ์˜๊ฐ์„ ๋ฐ›์€ ๊ธฐ๋Šฅ์ด ํ•˜๋‚˜๊ฐ€ ์žˆ์—ˆ๋‹ค. ๋ฐ”๋กœ ๊ฐค๋Ÿญ์‹œ ์‚ฌ์šฉ์ž๋“ค์ด ์šฐ์ธก ํ•˜๋‹จ์˜ "์ด์ „" ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์„œ ํ‚ค๋ณด๋“œ๋ฅผ ์ข…๋ฃŒํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ๊ด€์ฐฐํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ทธ ์ž๋ฆฌ์— ํ‚ค๋ณด๋“œ๋ฅผ ์†์‰ฝ๊ฒŒ ์ข…๋ฃŒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ‚ค๋ณด๋“œ ์ข…๋ฃŒ ๋ฒ„ํŠผ์„ ๋ฐฐ์น˜ํ–ˆ๋‹ค.

์šฐ์ธก ํ•˜๋‹จ์˜ ํ‚ค๋ณด๋“œ ์ข…๋ฃŒ ๋ฒ„ํŠผ์„ ํƒญํ•˜๋ฉด ํ‚ค๋ณด๋“œ๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค.

๐Ÿง‘๐Ÿปโ€๐ŸŽจ Midjourney๋ฅผ ์ด์šฉํ•œ ์•ฑ ์•„์ด์ฝ˜ ์ƒ์„ฑ

ALT: Midjourney ์ด๋ฏธ์ง€๋“ค

์ƒ์„ฑ๋œ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ด๋ฏธ์ง€๋“ค

์•ฑ ์•„์ด์ฝ˜์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด Midjourney๋ผ๋Š” ๊ทธ๋ฆผ AI๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. ์š”์ฆ˜์—๋Š” ์ด๋Ÿฐ ๊ฒƒ์„ ํ”„๋กฌํ”„ํŠธ ์—”์ง€๋‹ˆ์–ด๋ง์ด๋ผ๊ณ  ํ•˜๋˜๋ฐ ๊ทธ๋ƒฅ ๋‹ค์–‘ํ•œ ํ‚ค์›Œ๋“œ๋“ค๋กœ ๊ทธ๋ฆผ์„ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ์ด ์ฆ๊ฑฐ์› ๋‹ค.

โ˜๏ธ Xcode Cloud๋ฅผ ์ด์šฉํ•œ CI/CD ์—ฐ๊ฒฐ

๋งˆ์ง€๋ง‰์œผ๋กœ 2022๋…„์— ์ถœ์‹œ๋œ Xcode Cloud๋ฅผ ์ด์šฉํ•ด CI/CD๋ฅผ ๊ตฌ์ถ•ํ–ˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜๋ฉด ๋งˆ์น˜ GitHub์— ๋ฆฌ์•กํŠธ ์ฝ”๋“œ๋ฅผ ํ‘ธ์‹œํ•˜๋ฉด Vercel์ด ์•Œ์•„์„œ ๋นŒ๋“œ ํ›„ ๋ฐฐํฌํ•ด์ฃผ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, Apple Xcode Cloud ์„œ๋ฒ„์—์„œ iOS ์•ฑ์ด ์ปดํŒŒ์ผ๋˜์–ด ๋ณด๊ด€๋œ๋‹ค. ์• ํ”Œ ์•„์ดํฐ ์•ฑ์˜ ๊ฒฝ์šฐ ์•ฑ์Šคํ† ์–ด ๋ฆฌ๋ทฐ๋ผ๋Š” ์ ˆ์ฐจ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์™„์ „ ์ž๋™ ๋ฐฐํฌ๊ฐ€ ๋˜์ง€๋Š” ์•Š๊ณ , ์•ฑ์Šคํ† ์–ด ์ฝ˜์†”์—์„œ ๋นŒ๋“œ๋ฅผ ์„ ํƒํ•˜๊ณ  ๋ฆฌ๋ทฐ ์š”์ฒญ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์•ผ ํ•˜์ง€๋งŒ, ์ด์ „์— ์—‘์Šค์ฝ”๋“œ์—์„œ ์•„์นด์ด๋ธŒ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด ์ˆ˜๋™์œผ๋กœ ์—…๋กœ๋“œํ•ด์•ผํ–ˆ๋˜ ๊ฒƒ์— ๋น„ํ•ด์„œ ํ›จ์”ฌ ํŽธํ•ด์กŒ๋‹ค.

์ด์™€ ๊ฐ™์ด ์•ฑ์Šคํ† ์–ด ์ฝ˜์†”์—์„œ GitHub์™€ ์—ฐ๋™๋œ ๋นŒ๋“œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ‘ธ์‹œ ์•Œ๋ฆผ๋„ ๋ณด๋‚ด์ค€๋‹ค.

๐Ÿ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์˜ค๋žœ๋งŒ์— iOS ๊ฐœ๋ฐœ์„ ํ•˜๋‹ˆ ์ฆ๊ฑฐ์› ๋‹ค. iOS ํ”Œ๋žซํผ์ด ํ›จ์”ฌ ์„ฑ์ˆ™ํ•ด์ง„ ๊ฒƒ์ด ๋А๊ปด์กŒ๋‹ค. ํŠนํžˆ๋‚˜ ํ™œ์ž ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•œ๊ธ€์ด ์ด๋ ‡๊ฒŒ ์•„๋ฆ„๋‹ต๊ตฌ๋‚˜๋ผ๋Š” ๊ฐ์ •์„ ๋งŽ์ด ๋А๊ผˆ๋‹ค. ๋ฌด์—‡๋ณด๋‹ค๋„ ๋ถ€๋ชจ๋‹˜๊ป˜ ์ฐธ ์ข‹์€ ์„ ๋ฌผ์„ ํ•ด๋“œ๋ฆฐ ๊ฒƒ ๊ฐ™์•„ ๊ธฐ๋ถ„์ด ์ข‹์•˜๋‹ค. ์ž‘์—…ํ•œ ๋งํฌ๋“ค์„ ์ฒจ๋ถ€ํ•˜๋ฉฐ ๊ธ€์„ ๋งˆ๋ฌด๋ฆฌํ•œ๋‹ค.

์•ฑ์Šคํ† ์–ด 5์  ๋ฆฌ๋ทฐ์™€ GitHub ์Šคํƒ€๋Š” ํฐ ์‘์›์ด ๋ฉ๋‹ˆ๋‹ค!