ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๋ฐ˜์‘ํ˜•

ios-og


๐ŸŽฏ MVC(Model - View - Controller)?


๐Ÿ’ฌ what is MVC?


  • ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹œ์Šคํ…œ ์•ˆ์˜ ๋ชจ๋“  ๊ฐ์ฒด๋Š” ์„ธ๊ฐ€์ง€์˜ ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • Model: What your application is about (but not how it is displayed)
      ์•ฑ์—์„œ UI์™€ ๋…๋ฆฝ์ ์ธ ๊ฐ์ฒด๋“ค๋กœ '๋ฌด์—‡'์— ํ•ด๋‹นํ•˜๋Š” ์š”์†Œ๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Concentration Game์—์„œ๋Š” ๊ฒŒ์ž„ ์ž์ฒด๋ฅผ ํ•  ์ค„ ์•„๋Š” ๋ถ€๋ถ„์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ์นด๋“œ๊ฐ€ ๋งค์น˜๋˜๋Š”์ง€, ์นด๋“œ๋ฅผ ์ œ๊ฑฐํ•ด์•ผํ•˜๋Š”์ง€, ์–ธ์ œ ์นด๋“œ๋ฅผ ๋’ค์ง‘์–ด์•ผํ•˜๋Š”์ง€ ๊ฐ™์€ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. (Knowledge of the game)

    • Controller: How your Model is presented to the user (UI Logic)

      ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์œ„์— ์ •์˜๋œ ๋ชจ๋ธ์ด ์–ด๋–ป๊ฒŒ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๋Š”์ง€์— ๊ด€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

    • View: Controller's minions

      ์ผ๋ฐ˜์ ์ธ UI ์š”์†Œ๋“ค์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด, UIButton, UIViewController, UILabel ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. Controller๊ฐ€ Model๊ณผ ์—ฐ๋™ํ•˜์—ฌ ์•ฑ์— ํ•„์š”ํ•œ ๋ฌด์–ธ๊ฐ€๋ฅผ ๊ฐ€์ ธ์™€ UI์— ๋ณด์—ฌ์ค„ ๋•Œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.


    • ์„ธ ์š”์†Œ์˜ ํ†ต์‹ ์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.


  • Controller๋Š” Model, View์™€ ์ง์ ‘์ ์œผ๋กœ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ฑฐ์˜ ๋™์‹œ์— ์–‘์ชฝ ๋ชจ๋‘์™€ ํ†ต์‹  ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Model์€ UI Independent ํ•˜๋ฏ€๋กœ View์™€ ํ†ต์‹ ์ด ๋ถˆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ View๋Š” ๋ฒ„ํŠผ ๋“ฑ UI ์š”์†Œ๋“ค์˜ ์ง‘ํ•ฉ์ธ๋ฐ UI๋ฒ„ํŠผ์ด ์ด ์•ฑ์ด ์–ด๋–ค ๊ธฐ๋Šฅ์„ ํ•˜๋Š”์ง€ ์•„๋Š” ๊ฒƒ์€ ๋ง์ด๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.


    โ˜Ž๏ธ View์™€ Controller์˜ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜


    ์—ฌ๊ธฐ์„œ ์ž์„ธํžˆ ์‚ดํŽด๋ณด์•„์•ผ ํ•˜๋Š” ๊ฒƒ์€ View๊ฐ€ Controller์— ํ†ต์‹ ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. 1๊ฐ•์—์„œ ๋งŒ๋“  Concentration game์„ ์˜ˆ๋กœ ๋“ค๋ฉด, ํ™”๋ฉด์—์„œ ์นด๋“œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๊ฒฝ์šฐ Controller๊ฐ€ ์ด๋ฅผ ์•Œ์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ •๋ณด๋ฅผ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ View ๊ฐ์ฒด๋Š” ์ผ๋ฐ˜์ ์ธ ๋ทฐ ๊ฐ์ฒด๋กœ Controller์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์—†๋Š” ๋ธ”๋ผ์ธ๋“œ ์ƒํƒœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค(Communication is "blind" and structured). ๋‹ค์‹œ ๋งํ•˜๋ฉด, View๊ฐ€ Controller์™€ ํ†ต์‹ ํ•  ๋•Œ, ์ƒ๋Œ€ Controller๊ฐ€ ์ง‘์ค‘๋ ฅ ๊ฒŒ์ž„ Controller๋ผ๋Š” ์‚ฌ์‹ค์„ ์•Œ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ structured์˜ ์˜๋ฏธ๋Š” View๋Š” ์ผ๋ฐ˜์ ์ธ ๊ฐ์ฒด(generic object)๋กœ ํŠน์ • ์ปจํŠธ๋กค๋Ÿฌ์™€ ์–ด๋–ป๊ฒŒ ๋Œ€ํ™”๋ฅผ ํ• ์ง€ ๋ฏธ๋ฆฌ ์ •ํ•ด์ ธ ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ ๊ตฌ์กฐํ™” ๋˜์–ด์žˆ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ†ต์‹ ์„ ์œ„ํ•ด ํ•„์š”ํ•œ ๊ฒƒ์ด target-action ์ž…๋‹ˆ๋‹ค.



    ์ผ๋ฐ˜ ๊ฐ์ฒด(๊ฒŒ์ž„์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” Controller์™€ ๋‹ค๋ฅด๊ฒŒ ํŠน์ •๋˜์–ด์žˆ์ง€ ์•Š์€)์ธ View๊ฐ€ Controller์™€ ๋Œ€ํ™”ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” target์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ง‘์ค‘๋ ฅ ๊ฒŒ์ž„์—์„œ ์นด๋“œ์—์„œ Ctrl + ๋“œ๋ž˜๊ทธ๋กœ touchCard๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ ์ด๊ฒƒ์ด target์ด ๋ฉ๋‹ˆ๋‹ค. Controller๊ฐ€ ํ•ด์•ผํ•˜๋Š” ์ผ์€ ์ž์‹ ์—๊ฒŒ ํƒ€๊ฒŸ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฆ‰, touchCard ๊ฐ™์€ ๋ฉ”์†Œ๋“œ๋ฅผ Controller์—์„œ ๋งŒ๋“ค์–ด๋‘๋ฉด UIButton ๋“ฑ์—์„œ ํ˜ธ์ถœ์„ ๋ฐ›์„ ๋•Œ๋งˆ๋‹ค Action์„ ํ•ด๋‹น target์— ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” ์ € Action์ด ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.



    ์˜ˆ๋ฅผ ๋“ค์–ด ScrollView๋ฅผ ์ƒ๊ฐํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ UI์—์„œ ์Šคํฌ๋กค์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ(e.g. ๋๊นŒ์ง€ ์Šคํฌ๋กค ํ–ˆ๋Š”์ง€, ์—ฌ๊ธฐ๋ฅผ ์Šคํฌ๋กค ํ•ด๋„ ๋˜๋Š”์ง€ ๋“ฑ) ํ•„์š”ํ•œ ๊ฐœ๋…์ด Delegate์ž…๋‹ˆ๋‹ค.


    ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๋Š” ScrollView ์•ˆ์— ์žˆ๋Š” var์ž…๋‹ˆ๋‹ค. ์ด var๋Š” ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฐ์ฒด๋Š” ํŠน์ • ๋ฉ”์„ธ์ง€๋“ค์— ๋ฐ˜์‘ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์ด๋Ÿฐ ๋ฉ”์„ธ์ง€๋“ค์€ will, should, did๋กœ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค(I will scroll to here etc.). ์ปจํŠธ๋กค๋Ÿฌ๋Š” ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•ด์„œ ScrollView์—๊ฒŒ ์ž์‹ ์ด ScrollView์˜ Delegate๋ผ๊ณ  ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด, Scroll View๊ฐ€ ์•„๋Š” ๊ฒƒ์€ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ will, should, did๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค๋Š” ์‚ฌ์‹ค ๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ ์™ธ์— ๊ฒŒ์ž„๊ณผ ๊ด€๋ จ๋œ ๋‹ค๋ฅธ ์ •๋ณด(e.g. ์ปจํŠธ๋กค๋Ÿฌ์˜ ํด๋ž˜์Šค)๋Š” ์ผ์ ˆ ์•Œ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

    View๋Š” ์ž๊ธฐ๊ฐ€ ๋ณด์—ฌ์ค„ ๋‚ด์šฉ์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚ผ ์ •๋ณด๋ฅผ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋กœ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. iPod์—์„œ ์Œ์•…์ด 50,000 ๊ณก์ด ์žˆ์„ ๋•Œ ๊ทธ ๋ฆฌ์ŠคํŠธ๋ฅผ ListView์—์„œ ๊ฐ€์ง€๊ณ  ์žˆ์„ ์ด์œ ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ Controller์—์„œ ์ด๋Ÿฌํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ๋ฐ›์•„์˜ฌ ํ•„์š”๊ฐ€ ์žˆ๊ณ  ๊ทธ ๋•Œ ํ•„์š”ํ•œ ๊ฒƒ์ด data source๋ผ๋Š” ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ์ž…๋‹ˆ๋‹ค.

    data source์™€ delegate๋Š” ๋น„์Šทํ•˜์ง€๋งŒ ์„œ๋กœ ๋‹ค๋ฅธ ๋ฉ”์†Œ๋“œ๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋ฟ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ View๊ฐ€ Controller์™€ ํ•  ์ˆ˜ ์žˆ๋Š” Communication์€ ๊ตฌ์กฐ์ (structured)์ด๊ณ  ๋ฏธ๋ฆฌ ์ •์˜(predefined)๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

    ์œ„์˜ ์ด์•ผ๊ธฐ๋ฅผ ์ข…ํ•ฉํ•˜๋ฉด MVC์—์„œ Controller์˜ ์—ญํ• ์€ ๋ชจ๋ธ์˜ ์ •๋ณด๋ฅผ ํ•ด์„ํ•˜๊ณ  ๊ตฌ์„ฑํ•ด์„œ ๋ทฐ์—๊ฒŒ ์ฃผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.


    โ˜Ž๏ธ Model๊ณผ Controller์˜ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜


    ๋ชจ๋ธ ์—ญ์‹œ Controller์™€ ์ง์ ‘์ ์œผ๋กœ ๋Œ€ํ™”ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ View์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Model๋„ Controller์™€ ํ†ต์‹ ํ•ด์•ผํ•  ์ƒํ™ฉ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์„œ ๊ด€๋ จ๋œ UI๋“ค์ด ๊ทธ๊ฑธ ์—…๋ฐ์ดํŠธํ•ด์•ผํ•  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ ํ†ต์‹ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ต์ˆ˜๋‹˜์€ Radio Station Model(๋ผ๋””์˜ค ๋ฐฉ์†ก๊ตญ ๋ชจ๋ธ)์ด๋ผ๊ณ  ํ‘œํ˜„ํ•˜์‹ญ๋‹ˆ๋‹ค.



    ์ด๋ ‡๊ฒŒ Model์— ๋ผ๋””์˜ค ์†ก์‹ ๊ธฐ์ฒ˜๋Ÿผ ๋ชจ๋ธ์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•ด์•ผํ•  ๋งค์ฒด๊ฐ€ ์žˆ๊ณ , Controller๋Š” ๊ทธ ์ฃผํŒŒ์ˆ˜๋ฅผ Model์˜ ์†ก์‹ ๊ธฐ์— ๋งž์ถฐ๋†“์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ณ€๊ฒฝ์ด ์ƒ๊ฒผ์„ ๋•Œ Controller๋Š” ์ดˆ๋ก์ƒ‰ ํ™”์‚ดํ‘œ๋ฅผ ํ†ตํ•ด ๋ฐ”๋€ ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์ด ๋ผ๋””์˜ค ์†ก์‹ ๊ธฐ๋ฅผ iOS์—์„œ๋Š” Notification์ด๋‚˜ KVO(Key Value Observing)์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.


    ๐Ÿ“ฑ multiple MVCs in iPhone or iPad


    • ์•„์ดํฐ์˜ ์•ฑ ํ•˜๋‚˜๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋งŽ์€ ํ™”๋ฉด๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ•˜๋‚˜์˜ ์•ฑ์„ ํ•˜๋‚˜์˜ MVC์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋งŽ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๋„์‹ํ™”ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

      ์–ด๋–ค Controller๊ฐ€ ๋‹ค๋ฅธ Controller์™€ ํ†ต์‹ ํ•  ๋•Œ์—๋Š” ๊ทธ ๋Œ€์ƒ์ด Controller์ง€๋งŒ View์ฒ˜๋Ÿผ ์ทจ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๊ฒƒ๋“ค์€ ์ผ๋ฐ˜์ (generic)์ด๊ณ  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ(reusable) ์ปดํฌ๋„ŒํŠธ์ฒ˜๋Ÿผ ํ–‰๋™ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์•„๋ž˜์™€ ๊ฐ™์€ ๊ตฌ์กฐ๋กœ ํ”„๋กœ๊ทธ๋žจ์ด ๋™์ž‘ํ•œ๋‹ค๋ฉด

      UI์—์„œ ๋ฌด์–ธ๊ฐ€๊ฐ€ ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ ์–ด๋–ค ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ธ์ง€, ์–ด๋–ค ๋ชจ๋ธ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ์—ˆ๋Š”์ง€ ๋“ฑ์„ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋””๋ฒ„๊ทธ, ์œ ์ง€ ๋ณด์ˆ˜ํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.


      ๐Ÿ“ Concentration Game์— MVC ์ ์šฉํ•ด๋ณด๊ธฐ


      • MVC ์ค‘ V, C๋Š” ์ด๋ฏธ ๊ตฌํ˜„์ด ๋˜์žˆ๊ธฐ ๋•Œ๋ฌธ์— Model์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ์„  swiftํŒŒ์ผ์„ ํ•˜๋‚˜ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค. ์ด ๋•Œ, SwiftํŒŒ์ผ๋ช…์€ ๊ทธ ํŒŒ์ผ์˜ ํด๋ž˜์Šค ์ค‘ ๊ฐ€์žฅ ๋Œ€ํ‘œ๋  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์˜ ์ด๋ฆ„์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” Concentration์ด๋ผ๊ณ  ๋ช…๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค.

        ์ƒˆ๋กœ์šด ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ์—๋Š” ํ•ญ์ƒ ์ด๊ฒƒ์˜ ๊ณต๊ฐœ API๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์ƒ๊ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ API๋ž€ Application Programming Interface๋กœ ํด๋ž˜์Šค ์•ˆ์˜ ๋ชจ๋“  ๋ฉ”์†Œ๋“œ์™€ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜์˜ ๋ฆฌ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Public API๋ž€ ๋‹ค๋ฅธ ํด๋ž˜์Šค๋“ค์˜ ์‚ฌ์šฉ์„ ํ—ˆ๋ฝํ•œ ๋ฉ”์†Œ๋“œ์™€ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋“ค์ž…๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ์ด ํด๋ž˜์Šค๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

        Concentration ๊ฒŒ์ž„์—์„œ ํ•„์š”ํ•œ ๊ณต๊ฐœ API๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

        import Foundation
        
        class Concentration {
        
            var cards: Array<Card>
        
            func chooseCard(at index: Int) {
        
            }
        }

        ์—ฌ๊ธฐ์„œ cards๋Š” Card๋ผ๋Š” ์•„์ง ์ƒ์„ฑ๋˜์ง€ ์•Š์€ ๊ฐ์ฒด์˜ ๋ฐฐ์—ด์ด๊ณ , chooseCard๋Š” ์นด๋“œ๋ฅผ ๊ณ ๋ฅด๋Š” ๋ฉ”์†Œ๋“œ์ธ๋ฐ Card๋กœ ์ง์ ‘ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ index๋ฅผ ํ†ตํ•ด cards์—์„œ ๊บผ๋‚ด์˜ค๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

      • ์ด์ œ Card๋ฅผ ์ •์˜ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Card.swift๋ฅผ ์ƒ์„ฑํ•œ ํ›„ Card๋ฅผ ๋งŒ๋“œ๋Š”๋ฐ ์ด๋ฒˆ์—๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ์กฐ์ฒด์™€ ํด๋ž˜์Šค์˜ ์ฐจ์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

        1. ๊ตฌ์กฐ์ฒด(struct)๋Š” ์ƒ์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
        2. ๊ตฌ์กฐ์ฒด๋Š” ๊ฐ’ํƒ€์ž…์ด๊ณ  ํด๋ž˜์Šค๋Š” ์ฐธ์กฐํƒ€์ž…์ž…๋‹ˆ๋‹ค.

        ๊ฐ’ํƒ€์ž…์€ ์ธ์ž๋กœ ๋ณด๋‚ด๊ฑฐ๋‚˜, ๋ฐฐ์—ด์— ๋„ฃ๊ฑฐ๋‚˜, ๋‹ค๋ฅธ ๋ณ€์ˆ˜์— ํ• ๋‹นํ•  ๋•Œ์—๋„ ๋ณต์‚ฌ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜๋ฉด ๊ตฌ์กฐ์ฒด๋ฅผ ํ†ตํ•ด ํ”„๋กœ๊ทธ๋žจ์„ ์งœ๋ฉด ๋งค๋ฒˆ ๋ณต์‚ฌ๊ฐ€ ์ผ์–ด๋‚˜์„œ ๋น„ํšจ์œจ์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, Swift๋Š” ๊ฐ’ํƒ€์ž…์„ ์ƒ์„ฑํ•  ๋•Œ๋งˆ๋‹ค ๋ฐ”๋กœ ๋ณต์‚ฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฐธ์กฐ์˜ ํ˜•ํƒœ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ฐ€ ๊ฐ’์˜ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚  ๋•Œ ์‹ค์ œ๋กœ ๋ณต์‚ฌ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ "Copy on write semantics"๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

        struct Card {
            var isFaceUp = false
            var isMatched = false
            var identifier: Int
        }

        Card๋Š” ์œ„์™€ ๊ฐ™์€ ์š”์†Œ๋ฅผ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์šฐ์„  ๋ชจ๋“  ๊ฐ’์€ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•ด์•ผํ•˜๋ฏ€๋กœ ๋ณ€์ˆ˜(var)๋กœ ์„ ์–ธ๋˜๊ณ , ์นด๋“œ๊ฐ€ ๋’ค์ง‘ํ˜€์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด isFaceUp, ๋‹ค๋ฅธ ์นด๋“œ์™€ ๋งค์นญ์ด ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด isMatched๊ฐ€ ํ•„์š”ํ•œ๋ฐ ์ด ๋•Œ ๋งค์น˜๊ฐ€ ๋˜๋Š” ์นด๋“œ์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์‹๋ณ„์ž๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜๊ธฐ ๊ฐ„ํŽธํ•œ Intํ˜•์œผ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

        ๊ตฌํ˜„ํ•œ ์นด๋“œ์— ์ด๋ฏธ์ง€๋กœ ์ด๋ชจํ‹ฐ์ฝ˜์ด ๋“ค์–ด์žˆ์œผ๋ฏ€๋กœ String ํ”„๋กœํผํ‹ฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ง€๊ธˆ ๋งŒ๋“œ๋Š” Card๋Š” ๋ชจ๋ธ์ด๊ณ  ๋ชจ๋ธ์€ UI independentํ•˜๋ฏ€๋กœ ์ด๋ชจํ‹ฐ์ฝ˜์ด๋‚˜ jpeg ์ด๋ฏธ์ง€ ๊ฐ™์€ ํŒŒ์ผ์€ ์—ฌ๊ธฐ ์กด์žฌํ•ด์„œ๋Š” ์•ˆ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ๋“ค์€ "How do u display the cards"์™€ ๊ด€๋ จ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

      • ์ด์ œ Card๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์‹œ Concentration ํด๋ž˜์Šค๋กœ ๋Œ์•„๊ฐ€์„œ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

        class Concentration {
        
            var cards = Array<Card>()
        
            func chooseCard(at index: Int) {
        
            }
        }

        ์•„๊นŒ์™€ ๋‹ค๋ฅธ ์ ์€ Card๋ผ๋Š” ํƒ€์ž…์ด ์ƒ์„ฑ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— cards์˜ ์„ ์–ธ์—์„œ ๋ฌธ์ œ๊ฐ€ ์—†๊ณ , ์ดˆ๊ธฐํ™”๊ฐ€ ์•ˆ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํƒ€์ž… ๋ช…์‹œ๊ฐ€ ์•„๋‹ˆ๋ผ cards๋ฅผ ์ดˆ๊ธฐํ™” ํ•ด์ค๋‹ˆ๋‹ค. ์ด ๋•Œ, Array๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†๋Š” init์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์„œ ()๋ฅผ ๋ถ™์—ฌ empty Array๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

        Array์˜ init์—๋Š” ๋‹ค์–‘ํ•œ ๊ฒƒ๋“ค์ด ์žˆ๋Š”๋ฐ, ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉํ•  ์‚ฌ์ด์ฆˆ๊ฐ€ ์ •ํ•ด์ ธ ์žˆ๋‹ค๋ฉด Array์˜ ํฌ๊ธฐ๋ฅผ ์ •ํ•ด์ฃผ๋Š” init, ๋‹ค๋ฅธ ๋ฐฐ์—ด์—์„œ ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด์„œ ๊ทธ ํ•ญ๋ชฉ์„ ๋ณต์‚ฌํ•ด์˜ค๋Š” init ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

      • ์ด์ œ View Controller์—์„œ Model๊ณผ ํ†ต์‹ ์„ ์œ„ํ•ด ์ดˆ๋ก์ƒ‰ ํ™”์‚ดํ‘œ๋ฅผ ๋งŒ๋“œ๋Š” ์ž‘์—…์„ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

        class ViewController: UIViewController {
        
            var game: Concentration
        
            var flipCount = 0 {
                didSet {
            ...

        game์ด๋ผ๋Š” Concentration ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์–ด ์ด์ œ Concentration ์žˆ๋Š” ์š”์†Œ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. game์„ ์œ„์™€ ๊ฐ™์ด ์„ ์–ธํ•ด์ฃผ๋ฉด ์—ญ์‹œ๋‚˜ ์ดˆ๊ธฐํ™” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์ดˆ๊ธฐํ™”๋ฅผ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

        var game = Concentration()

        Concentration ํด๋ž˜์Šค๋ฅผ ์„ ์–ธํ•  ๋•Œ init์„ ์ •์˜ํ•ด์ฃผ์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ๊ฐ€ ๋‚˜์•ผํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์™œ ๊ทธ๋Ÿด๊นŒ์š”?

        Swift๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ class๋ฅผ ์„ ์–ธํ•  ๋•Œ, parameter๊ฐ€ ๋น„์–ด์žˆ๋Š” init์„ ์ž๋™์œผ๋กœ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

      • ์ด ๊ฒŒ์ž„์„ ์™„์„ฑํ–ˆ์„ ๋•Œ ์–ผ๋งˆ๋‚˜ ๋งŽ์€ ์นด๋“œ๊ฐ€ ์žˆ๋Š”์ง€ ์ƒ๊ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ž๋™ํ™” ๋œ init์ด ์•„๋‹Œ ์ปค์Šคํ…€ํ•œ init์„ ๋งŒ๋“ค์–ด์ค„ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. init์•ˆ์— ์นด๋“œ ํ•˜๋‚˜๋ฅผ ์ƒ์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

        init(numberOfPairsOfCards: Int) {
                let card = Card() // ์—๋Ÿฌ ๋ฐœ์ƒ
            }

        Concentration์—์„œ๋Š” ์ž˜ ๋˜์—ˆ๋˜ ์ดˆ๊ธฐํ™”๊ฐ€ ์™œ Card์—์„œ๋Š” ์•ˆ๋ ๊นŒ์š”? ๊ทธ ์ด์œ ๋Š” Card๊ฐ€ struct์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. struct ์—ญ์‹œ free initializer๋ฅผ ๊ฐ–์ง€๋งŒ class์™€๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค. struct๊ฐ€ ๋ฐ›๋Š” ๊ณต์งœ ์ด๋‹ˆ์…œ๋ผ์ด์ €๋Š” ๋ชจ๋“  ๋ณ€์ˆ˜๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ์ดˆ๊ธฐํ™” ๋˜์–ด ์žˆ๋Š” ๊ฐ’๋„ ๋‹ค์‹œ ์ดˆ๊ธฐํ™” ํ•ด์ค๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” Xcode์˜ ์ž๋™์™„์„ฑ ๊ธฐ๋Šฅ์„ ์ด์šฉํ•œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

        init(numberOfPairsOfCards: Int) {
                let card = Card(isFaceUp: <#T##Bool#>, isMatched: <#T##Bool#>, identifier: <#T##Int#>)
            }

        ์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์ด๋ฏธ ์ดˆ๊ธฐํ™” ๋˜์–ด์žˆ๋Š” isMatched ๋“ฑ์˜ ๋ฉ”์†Œ๋“œ ์—ญ์‹œ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. Card์˜ identifier๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด init์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

        init(identifier: Int) {
                identifier = identifier // ์—๋Ÿฌ ๋ฐœ์ƒ
            }

        ์™œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ• ๊นŒ์š”? ์šฐ์„  init์˜ ์ธ์ž์ธ indentifier๋Š” external name์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ identifier๊ฐ€ ์ •ํ™•ํžˆ ์–ด๋–ค ์˜๋ฏธ์ธ์ง€ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

        init(identifier i: Int) {
                identifier = i // ์—๋Ÿฌ ๋ฐœ์ƒ
            }

        ํ•˜์ง€๋งŒ i๋Š” ๋งค์šฐ ์•ˆ์ข‹์€ ๋„ค์ด๋ฐ์ž…๋‹ˆ๋‹ค. ์•„๋ฌด ์˜๋ฏธ๋ฅผ ๋‹ด๊ณ  ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

        init(identifier: Int) {
                self.identifier = identifier 
            }

        self.identifier๋Š” ๊ฐ์ฒด ์ž์‹ ์ด ๊ฐ€์ง€๊ณ ์žˆ๋Š” identifier, ์—ฌ๊ธฐ์„œ๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

      • ์ด๋ฒˆ์—” Concentration์— ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Swift์˜ for๋ฌธ์€ ์‹œํ€€์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. sequence๋ž€ ์–ด๋”˜๊ฐ€์—์„œ ์‹œ์ž‘ํ•ด์„œ ๋‹ค์Œ์œผ๋กœ, ๊ทธ ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ€๋ฉด์„œ ๋Œ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. Array, String ๋“ฑ์ด Sequence์˜ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. Concentration์˜ init์„ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

        init(numberOfPairsOfCards: Int) {
                for identifier in 1...numberOfPairsOfCards {
                    let card = Card(identifier: identifier)
                    let matchingCard = card
                    cards.append(card)
                    cards.append(matchingCard)
                }
            }

        cards ๋ฐฐ์—ด์— ์ƒ์„ฑ๋œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์œ„์—์ฒ˜๋Ÿผ ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

        init(numberOfPairsOfCards: Int) {
                for identifier in 1...numberOfPairsOfCards {
                    let card = Card(identifier: identifier)
                    cards.append(card)
                    cards.append(card)
                }
            }

        matchingCard๋ผ๋Š” ์ƒ์ˆ˜๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐฐ์—ด์— ๋„ฃ๊ฑฐ๋‚˜ ๋บ„ ๋•Œ์—๋„ ์นด๋“œ๋ฅผ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ฐธ์กฐ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ณต์‚ฌ์ด๋ฏ€๋กœ ์œ„์˜ ๋‘ ์ฝ”๋“œ๋Š” ์™„์ „์ด ๊ฐ™์€ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ˜„์žฌ init์˜ for๋ฌธ ์•ˆ์—๋Š” ์ด 3๊ฐœ์˜ card๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. 3๊ฐœ์˜ ์นด๋“œ๋Š” ์ฒ˜์Œ ์ƒ์ˆ˜๋กœ ์„ ์–ธ๋œ card, ์ฒซ๋ฒˆ์งธ cards์— append๋  ๋•Œ ๋ณต์‚ฌ๋˜๋Š” card, ๋‘๋ฒˆ์งธ cards์— append๋  ๋•Œ ๋ณต์‚ฌ๋˜๋Š” ์นด๋“œ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๊ตฌ์กฐ์ฒด๊ฐ€ ๊ฐ’ํƒ€์ž…์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋˜‘๊ฐ™์€ ์ฝ”๋“œ๋ฅผ 2๋ฒˆ ์ผ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ cards์— ๋“ค์–ด๊ฐ„ ๋‘ card๋Š” ๋‘๊ฐœ์˜ ๋‹ค๋ฅธ ์นด๋“œ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‘ ์ค„์˜ ๊ฐ™์€ ์ฝ”๋“œ๋Š” ๋ณด๊ธฐ ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

        cards += [card, card]

        ์ด๊ฒƒ์€ ํŒŒ์ด์ฌ์˜ extend์™€ ๊ฐ™์€ ์—ญํ• ์ด๋ฉฐ, ์‹ค์ œ๋กœ ํŒŒ์ด์ฌ์—์„œ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ Array๋ฅผ extend ํ•ด์ค๋‹ˆ๋‹ค.

      • ์œ„์˜ ์ฝ”๋“œ๋Š” ์˜จ์ „ํžˆ ์ž‘๋™ํ•˜์ง€๋งŒ identifier = identifier๋ผ๋Š” ๊ตฌ๋ฌธ์ด ๋งˆ์Œ์— ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ์ง‘์ค‘๋ ฅ ๊ฒŒ์ž„์ด ์ง์ ‘ ์นด๋“œ์˜ ์‹๋ณ„์ž๋ฅผ ์ง€์ •ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ง‘์ค‘๋ ฅ ๊ฒŒ์ž„์€ ์‹๋ณ„์ž๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์œ ์ผํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ identifier๋ฅผ ์‚ญ์ œํ•˜๊ณ  Card๊ฐ€ ์ง์ ‘ identifier๋ฅผ ์„ค์ •ํ•˜๊ฒŒ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

        identifier๋Š” Card๊ฐ€ ๋ช‡๊ฐœ๊ฐ€ ์ƒ์„ฑ๋˜๋“  ๊ทธ๋“ค์„ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ๋ถ„์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ƒˆ๋กœ์šด ์นด๋“œ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์—ฌ๊ธฐ์„œ ์ •์  ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ •์  ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Card์—๊ฒŒ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. Type Card์—๋งŒ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. static์„ ํ†ตํ•ด identifier๋ฅผ ๋งŒ๋“ค๊ณ  identifier๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

        static var identifierFactory = 0
        
        static func getUniqueIdentifier() -> Int {
          Card.identifierFactory += 1
          return Card.identifierFactory
        }

        ์—ฌ๊ธฐ์—์„œ getUniqueIdentifier ๋ฉ”์†Œ๋“œ๋Š” ์ •์ ๋ฉ”์†Œ๋“œ์ด๋ฏ€๋กœ ์ •์  ๋ฉ”์†Œ๋“œ ์•ˆ์—์„œ๋Š” Card. ์—†์ด๋„ ์ •์  ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ Card.์„ ์ƒ๋žตํ•ด์ค˜๋„ ๋ฉ๋‹ˆ๋‹ค.

        static func getUniqueIdentifier() -> Int {
                identifierFactory += 1
                return identifierFactory
            }

        ์ด ๋ณ€๊ฒฝ๋œ identifier๋ฅผ ์ดˆ๊ธฐํ™”์— ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์œ„์˜ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

        init() {
                self.identifier = Card.getUniqueIdentifier()
            }

        ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ƒˆ๋กœ์šด ์นด๋“œ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ๋งˆ๋‹ค identifierFactory๊ฐ€ 1์”ฉ ๋Š˜์–ด๋‚˜๋ฉฐ ๊ทธ ์นด๋“œ์˜ ๊ณ ์œ  ์ˆซ์ž๋ฅผ ๋งค๊ฒจ์ค๋‹ˆ๋‹ค.

      • ๊ต์ˆ˜๋‹˜๊ป˜์„œ ์นด๋“œ๋ฅผ ์„ž๋Š” ์ฝ”๋“œ๋ฅผ ๊ณผ์ œ๋กœ ๋‚ด์ฃผ์…จ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

        init(numberOfPairsOfCards: Int) {
                for _ in 1...numberOfPairsOfCards {
                    let card = Card()
                    cards += [card, card]
                }
                cards.shuffle() // ๋‚ด์žฅํ•จ์ˆ˜
            }

        ๋‚ด์žฅํ•จ์ˆ˜ shuffle์˜ ๋ณต์žก๋„๋Š” O(N)์ž…๋‹ˆ๋‹ค. Swift์—์„œ Shuffle์€ fisher yates shuffle ์ด๋ผ๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ ํ•จ์ˆ˜๋Š” WWDC 2018์— ์†Œ๊ฐœ๋œ ๊ฒƒ์œผ๋กœ ํ•ด๋‹น ๊ฐ•์˜๊ฐ€ 2017๋…„ ๊ณผ์ •์ด๋ผ shuffle์ด ์—†์„ ๋•Œ ๊ณผ์ œ๋ฅผ ๋‚ด์ฃผ์‹  ๊ฒƒ์œผ๋กœ ์ถ”์ •๋ฉ๋‹ˆ๋‹ค. fisher yates๋Š” ์ธ๋ฑ์Šค๋ฅผ 0๋ถ€ํ„ฐ 1์”ฉ ์ฆ๊ฐ€์‹œํ‚ค๋ฉฐ ๋ฐฐ์—ด์˜ i ๋ฒˆ์งธ ์š”์†Œ์™€ ๋žœ๋ค์œผ๋กœ ๋ฝ‘ํžŒ j ๋ฒˆ์งธ ์š”์†Œ๋ฅผ swapํ•˜๋Š” ์‹์œผ๋กœ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

      • ์ด์ œ Controller์—์„œ game์„ ์ •์˜ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ game์€ Controller๊ฐ€ Concentration์„ ์ด์šฉํ•ด ํ•œ๊ฐœ์˜ ๊ฒŒ์ž„์„ ์ƒ์„ฑํ•ด์ฃผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

        var game = Concentration(numberOfPairsOfCards: (cardButtons.count + 1) / 2)

        ์ง€๊ธˆ ๋งŒ๋“œ๋Š” ์˜ˆ์‹œ์˜ ๊ฒฝ์šฐ ์นด๋“œ๊ฐ€ 4๊ฐœ์ด๋ฏ€๋กœ ๋‹จ์ˆœํžˆ 4๋ผ๊ณ  ์ ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒ๋˜๋ฉด ์นด๋“œ ์ˆ˜๊ฐ€ ๋ฐ”๋€”๋•Œ๋งˆ๋‹ค ๋“ค์–ด์™€์„œ ๋ณ€๊ฒฝํ•ด์ฃผ์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— cardButtons Array์˜ ํฌ๊ธฐ๋กœ ์„ ์–ธํ•ด์ค๋‹ˆ๋‹ค. ์ด ๋•Œ cardButtons๊ฐ€ ํ™€์ˆ˜์ผ ์ˆ˜๋„ ์žˆ์œผ๋ฏ€๋กœ +1 ํ•ด์ค€ ํ›„ 2๋กœ ๋‚˜๋ˆ„์–ด์ค๋‹ˆ๋‹ค(์Œ์„ ์ด๋ฃจ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—). ์ด๋ ‡๊ฒŒ ๋งˆ๋ฌด๋ฆฌ๊ฐ€ ๋˜๋ฉด ์ข‹๊ฒ ์ง€๋งŒ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

        Cannot use instance member 'cardButtons' within property initializer; property initializers run before 'self' is available

        ์˜ค๋ฅ˜ ๋ฉ”์„ธ์ง€์˜ ์˜๋ฏธ๋Š” ํ”„๋กœํผํ‹ฐ ์ดˆ๊ธฐํ™”์—์„œ ์ธ์Šคํ„ด์Šค ๋ฉค๋ฒ„์ธ cardButtons๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. self๋ผ๋Š” ๊ฒƒ์€ ๋ชจ๋“  ์ดˆ๊ธฐํ™”๊ฐ€ ์™„๋ฃŒ๋˜๊ณ  ์ƒ๊ธฐ๋Š” ๊ฒƒ์ธ๋ฐ ์ดˆ๊ธฐํ™” ๋‹จ๊ณ„์—์„œ ์ด๋ฏธ ์ธ์Šคํ„ด์Šค ๋ฉค๋ฒ„๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ ์ƒ๊ธฐ๋Š” ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค. Swift์—์„œ ๋ฌด์—‡์„ ์‚ฌ์šฉํ•˜๋“  ์‚ฌ์šฉํ•˜๊ธฐ ์ „์—๋Š” ์™„์ „ํžˆ ์ดˆ๊ธฐํ™” ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ์ง€๋งŒ ์—ฌ๊ธฐ์—์„œ๋Š” lazy๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

      • ์œ„์˜ ์ฝ”๋“œ๋ฅผ lazyํ•˜๊ฒŒ ๋‹ค์‹œ ์„ ์–ธํ•ด์ค๋‹ˆ๋‹ค.

        lazy var game = Concentration(numberOfPairsOfCards: (cardButtons.count + 1) / 2)

        lazy๋Š” ๋ˆ„๊ฐ€ ์‚ฌ์šฉํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ์ฆ‰, game์ด๋ผ๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์‚ฌ์šฉํ•˜๊ธฐ ์ „๊นŒ์ง€ ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์š”์†Œ๋“ค์˜ ์ดˆ๊ธฐํ™”๊ฐ€ ๋จผ์ € ์ด๋ฃจ์–ด์ง€๊ณ ๋‚˜์„œ game์ด ํ•„์š”ํ•  ๋•Œ ์ดˆ๊ธฐํ™”๋˜๋ฏ€๋กœ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ์ด game์ด ์ดˆ๊ธฐํ™”๋  ๋•Œ๊นŒ์ง€ ์•„๋ฌด๋„ game ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ข€ ๋” ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜๋ฉด lazy๋Š” ์ด ๋ณ€์ˆ˜๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์—ˆ๋‹ค๊ณ  ์ณ์ค๋‹ˆ๋‹ค. ์ด lazy์—๋Š” ์ œ์•ฝ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค. lazy๊ฐ€ ๋˜๋ฉด didSet์„ ๊ฐ€์งˆ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. lazy ๋ณ€์ˆ˜๋Š” Property Observer๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.


    ๐Ÿ“ฑ touchCard ๋กœ์ง ๋ณ€๊ฒฝํ•˜๊ธฐ


    • ์ด์ œ ์œ„์— ๊ตฌํ˜„ํ•ด๋‘” game ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํ™”๋ฉด์—์„œ Card๋ฅผ ๋ˆŒ๋ €์„ ๊ฒฝ์šฐ์˜ ๋กœ์ง์„ ๋ณ€๊ฒฝํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. flipCard์˜ ๋ช…๋ น์„ game์—์„œ Card๋ฅผ ์„ ํƒํ•˜๋Š” ๋กœ์ง์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

        @IBAction func touchCard(_ sender: UIButton) {              
              flipCount += 1
              if let cardNumber = cardButtons.firstIndex(of: sender) {
      //            flipCard(withEmoji: emojiChoices[cardNumber], on: sender)
                  game.chooseCard(at: cardNumber)
                  updateViewFromModel()
              } else {
                  print("Error")
              }
          }

      Controller์—์„œ๋Š” ์„ ํƒ๋˜์—ˆ๋‹ค๊ณ ๋งŒ ์•Œ๋ ค์ฃผ๊ณ  ์นด๋“œ์˜ ์„ ํƒ์€ Model์—๊ฒŒ ๋งก๊ธฐ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

    • ์ด์ œ Model๋กœ๋ถ€ํ„ฐ View๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ View๋Š” Model๊ณผ ์•ฝ๊ฐ„ ์—ฐ๊ฒฐ์ด ๋˜์–ด์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค(little out of sync). ๊ทธ๋ž˜์„œ updateViewFromModel์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

      func updateViewFromModel() {
              for index in 0..<cardButtons.count {
      
              }
          }

      ๋ชจ๋“  ๋ฒ„ํŠผ์„ ๊ด€์ฐฐํ•  ๋•Œ for button in cardButtons๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. cardButtons๊ฐ€ sequence์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ cards๋Š” Card ํƒ€์ž…์˜ Array์ด๊ณ  ์—ฌ๊ธฐ์„œ Card๋ฅผ ๊บผ๋‚ด์˜ค๋Š” ๊ตฌ์กฐ๋กœ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— index๋กœ ์ ‘๊ทผํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์œ„์˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ cardButtons.count๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

      func updateViewFromModel() {
              for index in cardButtons.indices {
      
              }
          }

      indices(plural of index)๋Š” Array์˜ ๋ฉ”์†Œ๋“œ๋กœ Array์˜ ๋ชจ๋“  ์ธ๋ฑ์Šค์˜ ๊ณ„์ˆ˜ ๊ฐ€๋Šฅ ๋ฒ”์œ„๋ฅผ ๋ฐฐ์—ด๋กœ ๋ฆฌํ„ดํ•ด์ค๋‹ˆ๋‹ค.

      Summary of indices

      The indices that are valid for subscripting the collection, in ascending order.

      ์ด์ œ for๋ฌธ ์•ˆ์„ ์ฑ„์›Œ์ฃผ๋ฉด

      func updateViewFromModel() {
              for index in cardButtons.indices {
                  let button = cardButtons[index]
                  let card = game.cards[index]
                  if card.isFaceUp {
                      button.setTitle(emoji, for: UIControl.State.normal)
                      button.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
                  } else {
                      button.setTitle("", for: UIControl.State.normal)
                      button.backgroundColor = #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 1)
                  }
              }
          }

      ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. cardButtons์˜ ์ธ๋ฑ์Šค๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ๋ฒ„ํŠผ์„ ๊ฐ€์ ธ์™€์„œ button์œผ๋กœ, ํ•ด๋‹น ์œ„์น˜์˜ ์นด๋“œ๋ฅผ ๊ฐ€์ ธ์™€์„œ ์นด๋“œ๋กœ ์„ ์–ธํ•ด์ค๋‹ˆ๋‹ค. ์นด๋“œ๊ฐ€ ์œ„๋ฅผ ๋ณด๊ณ ์žˆ์œผ๋ฉด ๋ฒ„ํŠผ์„ ๊ทธ๋ฆผ๊ณผ ์ฃผํ™ฉ์ƒ‰์œผ๋กœ ์„ค์ •ํ•ด์ฃผ๊ณ , ์นด๋“œ๊ฐ€ ๋’ค์ง‘ํ˜€์žˆ๋‹ค๋ฉด ๊ทธ๋ฆผ์ด ์—†์ด ํ•˜์–€์ƒ‰์œผ๋กœ ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค. ๊ธฐ์กด์— ์ž‘์„ฑํ•ด์ฃผ์—ˆ๋˜ flipCard๋Š” ๋”์ด์ƒ ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ ์‚ญ์ œํ•ด์ค๋‹ˆ๋‹ค.

    • ์ด์ œ ๋งŒ๋“ค์–ด์ฃผ์–ด์•ผ ํ•˜๋Š” ๊ธฐ๋Šฅ์€ card๊ฐ€ ๋งค์น˜๋˜๊ณ  ๋‚˜๋ฉด ๋”์ด์ƒ ๊ทธ ์นด๋“œ๋Š” ์‚ฌ๋ผ์ ธ์•ผํ•ฉ๋‹ˆ๋‹ค. ๋งŒ๋“ค์–ด๋‘” isMatched๋ฅผ ์‚ฌ์šฉํ•ด ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

      for index in cardButtons.indices {
                  let button = cardButtons[index]
                  let card = game.cards[index]
                  if card.isFaceUp {
                      button.setTitle(emoji, for: UIControl.State.normal)
                      button.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
                  } else {
                      button.setTitle("", for: UIControl.State.normal)
                      button.backgroundColor = card.isMatched ? #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 0) : #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 1)
                  }
              }

      ์‚ผํ•ญ์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•ด ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. card๊ฐ€ isMatched ์ƒํƒœ๋ผ๋ฉด ํˆฌ๋ช…ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๊ณ  ์•„๋‹ˆ๋ผ๋ฉด ์›๋ž˜๋Œ€๋กœ ์ฃผํ™ฉ์ƒ‰์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

      ์—ฌ๊ธฐ์„œ setTitle์˜ emoji๋Š” ์‚ญ์ œํ–ˆ๋˜ flipCard ๋ฉ”์†Œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์—์„œ ๊ฐ€์ ธ์˜จ ๊ฒƒ์ด๋ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ž„์‹œ๋ฐฉํŽธ์œผ๋กœ ์—๋Ÿฌ๋ฅผ ์—†์• ๊ธฐ ์œ„ํ•ด ๋ฉ”์†Œ๋“œ๋ฅผ ํ•˜๋‚˜ ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

      func emoji(for card: Card) -> String {
              return "?"
          }
      
      ... button.setTitle(emoji(for: card), ...

      ํฐ ์˜๋ฏธ๋Š” ์—†์ง€๋งŒ ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“‚ chooseCard ๊ตฌํ˜„ํ•˜๊ธฐ (in Concentration)

  • ํ˜„์žฌ card๋Š” cards Array์—์„œ ์ธ๋ฑ์Šค๋กœ ์ ‘๊ทผํ•˜์—ฌ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์นด๋“œ๋ฅผ index๋กœ ๊ฐ€์ ธ์™€ isFaceUp์˜ ๊ฐ’์„ ๋ฐ˜๋Œ€๋กœ ํ•ด์ฃผ๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

    func chooseCard(at index: Int) {
            if cards[index].isFaceUp {
                cards[index].isFaceUp = false
            } else {
                cards[index].isFaceUp = true
            }
        }

    ์ด์ œ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ๋กœ ์•ฑ์„ ์‹คํ–‰์‹œ์ผœ๋ณด๋ฉด ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•จ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋‚จ์€ ๊ฒƒ์€ ๋ฌผ์Œํ‘œ ๋Œ€์‹ ์— ์ด๋ชจ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.


๐Ÿ˜ ์นด๋“œ์—์„œ Emoji ๋ณด์—ฌ์ฃผ๊ธฐ

  • ์˜ค๋ฅ˜๋ฅผ ์—†์• ๊ธฐ ์œ„ํ•ด ์ž„์‹œ๋ฐฉํŽธ์œผ๋กœ "?" ์ถœ๋ ฅํ–ˆ๋˜ emoji ๋ฉ”์†Œ๋“œ๋ฅผ ๋‹ค์‹œ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

    var emoji = [Int:String]()
    
    func emoji(for card: Card) -> String { // ์—๋Ÿฌ ๋ฐœ์ƒ
      let chosenEmoji = emoji[card.identifier]
      return chosenEmoji
    }

    emoji๋ผ๋Š” ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ์„ ์–ธํ•ด์ฃผ๊ณ  card.identifier๋กœ ์ด๋ชจํ‹ฐ์ฝ˜์„ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ๊ดœ์ฐฎ์€ ๋“ฏ ๋ณด์ด์ง€๋งŒ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ ์ฝ”๋“œ๋Š” String์„ ๋ฐ˜ํ™˜ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ’์€ String์ด ์•„๋‹Œ String?(optional String)์ž…๋‹ˆ๋‹ค. Dictionary๋Š” key๋กœ item์— ์ ‘๊ทผํ•˜์—ฌ value๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ตฌ์กฐ์ธ๋ฐ key๊ฐ€ ์—†์„ ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜ํ™˜๊ฐ’์€ ํ•ญ์ƒ optional๋กœ ๋‚˜์˜ต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์œ„์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด

    func emoji(for card: Card) -> String {
            if emoji[card.identifier] != nil {
                return emoji[card.identifier]!
            } else {
                return "?"
            }
        }

    ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. if let์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์กฐ๊ธˆ ๋‹ค๋ฅธ ํ‘œํ˜„์„ ์จ๋ณด๊ธฐ ์œ„ํ•ด ์œ„์˜ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์œ„์˜ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด 1์ค„๋กœ ์ž‘์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

    return emoji[card.identifier] ?? "?"

    ์œ„์˜ ๋‘ ์ฝ”๋“œ๋Š” ์ •ํ™•ํžˆ ๊ฐ™์€ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“‚ ๋”•์…”๋„ˆ๋ฆฌ์— emoji ์ถ”๊ฐ€ํ•˜๊ธฐ


  • ์ด์ œ ๋”•์…”๋„ˆ๋ฆฌ์— emoji๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. emoji๋ฅผ ์š”์ฒญ๋ฐ›์•˜์„ ๋•Œ emojiChoices์— ์žˆ๋Š” emoji ์ค‘์—์„œ ๋”•์…”๋„ˆ๋ฆฌ์— ์ด๋ชจํ‹ฐ์ฝ˜์ด ํ•„์š”ํ•  ๋•Œ์—๋งŒ ๋žœ๋คํ•˜๊ฒŒ ๋„ฃ์–ด์ฃผ๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๋žœ๋คํ•œ ๊ตฌํ˜„์„ ์œ„ํ•ด ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. arc4random_uniform๋Š” pseudo random generator์ž…๋‹ˆ๋‹ค. 0๋ถ€ํ„ฐ upper_bound๊นŒ์ง€์˜ ์ˆซ์ž ์‚ฌ์ด์—์„œ ์ž„์˜์˜ ์ˆซ์ž๋ฅผ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ ์ƒํ•œ์€ ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    func emoji(for card: Card) -> String {
            if emoji[card.identifier] == nil, emojiChoices.count > 0 {
                let randomIndex = Int(arc4random_uniform(UInt32(emojiChoices.count)))
                emoji[card.identifier] = emojiChoices.remove(at: randomIndex)
            }
            return emoji[card.identifier] ?? "?"
        }

    ์šฐ์„  arc4random ๋ฉ”์†Œ๋“œ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ Int๊ฐ€ ์•„๋‹Œ UInt32 ํƒ€์ž…์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. emojiChoices.count๋Š” ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ Int๊ฐ’์„ ์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‹คํ–‰ํžˆ UInt32๋Š” struct์ด๊ณ  initialzer์— Int๊ฐ’์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›๋Š” ๊ตฌํ˜„์ด ๋˜์–ด์žˆ์–ด์„œ ์œ„์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋žœ๋ค์œผ๋กœ ์ƒ์„ฑ๋œ ๊ฐ’ ์—ญ์‹œ UInt32์ด๊ณ  ์šฐ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ randomIndex๋Š” Array์˜ index๋กœ ์‚ฌ์šฉํ•ด์•ผํ•˜๋ฏ€๋กœ Int๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. Int ์—ญ์‹œ initializer์— UInt32๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›๋Š” ๊ตฌํ˜„์ด ๋˜์–ด์žˆ์–ด ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Int๋ฅผ ์•ž์— ๋ถ™์—ฌ ํ˜•๋ณ€ํ™˜์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

    emoji[card.identifier] = emojiChoices.remove(at: randomIndex)

    ๋งŒ์•ฝ emoji[card.identifier] = emojiChoices[randomIndex]๋กœ ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด ๋ฌด์—‡์ด ๋ฌธ์ œ์ผ๊นŒ์š”? ์ด๋ฏธ ์‚ฌ์šฉ๋œ ์ด๋ชจ์ง€๊ฐ€ ๊ณ„์†ํ•ด์„œ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊บผ๋‚ผ๋•Œ๋งˆ๋‹ค ๊บผ๋‚ด์ง„ ์ด๋ชจ์ง€๋Š” ํ›„๋ณด๊ตฐ์—์„œ ์ œ์™ธ๋˜๋Š” ๋กœ์ง์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. remove ๋ฉ”์†Œ๋“œ๋Š” ํ•ด๋‹น ์ธ๋ฑ์Šค์˜ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๋™์‹œ์— ๊ทธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ์œ„์™€ ๊ฐ™์ด ์ •์˜๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

    if emoji[card.identifier] == nil, emojiChoices.count > 0 {

    ์ด์ œ ๋ฌธ์ œ๋Š” ๋”์ด์ƒ ๊บผ๋‚ด์˜ฌ ์ˆ˜ ์žˆ๋Š” ํ›„๋ณด๊ตฐ์ด ์—†์„ ๋•Œ ์ž…๋‹ˆ๋‹ค. arc4random ํ•จ์ˆ˜๋Š” ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ๋Œ€๋กœ 0์—์„œ upperbound-1๊นŒ์ง€์˜ ์ˆซ์ž ์ค‘์— ๋žœ๋คํ•˜๊ฒŒ ํ•˜๋‚˜๋ฅผ ๊บผ๋‚ด์˜ค๋Š” Unsigned Int ๊ฐ’์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ 0 ์ดํ•˜์˜ ๊ฐ’์€ ๋‚˜์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. upperbound๋Š” ์–‘์ˆ˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์กฐ๊ฑด์„ ์ค„ ๋•Œ if ๋ฌธ์„ ๋‘๊ฐœ ์ž‘์„ฑํ•ด๋„ ๋˜์ง€๋งŒ ์œ„์—์ฒ˜๋Ÿผ ์‹ฌํ”Œํ•˜๊ฒŒ comma(,)๋กœ ๋ถ„๋ฆฌ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. comma๋Š” and ๋กœ์ง์˜ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ•น Concentration game ๋กœ์ง ์ž‘์„ฑํ•˜๊ธฐ


  • 2๊ฐ•์˜ ๋งˆ์ง€๋ง‰ ๋กœ์ง์„ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ ๋กœ์ง์€ ๊ฒŒ์ž„์— ๊ด€๋ จ๋œ ๊ฒƒ์œผ๋กœ Model์—์„œ๋งŒ ์ž‘๋™ํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์— Concentration์˜ chooseCard์— ์ž‘์„ฑํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

    var indexOfOneAndOnlyFaceUpCard: Int?
    
    func chooseCard(at index: Int) {
            if !cards[index].isMatched {
                if let matchIndex = indexOfOneAndOnlyFaceUpCard, matchIndex != index {
                    if cards[matchIndex].identifier == cards[index].identifier {
                        cards[matchIndex].isMatched = true
                        cards[index].isMatched = true
                    }
                    cards[index].isFaceUp = true
                    indexOfOneAndOnlyFaceUpCard = nil
                } else {
                    // either no cards or 2 cards are face up
                    for flipDownIndex in cards.indices {
                        cards[flipDownIndex].isFaceUp = false
                    }
                    cards[index].isFaceUp = true
                    indexOfOneAndOnlyFaceUpCard = index
                }
            }
        }

    indexOfOneAndOnlyFaceUpCard๋Š” ์œ ์ผํ•˜๊ฒŒ ๋’ค์ง‘ํžŒ ์นด๋“œ์˜ ์ธ๋ฑ์Šค์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ํ”„๋กœํผํ‹ฐ์ž…๋‹ˆ๋‹ค. ๋กœ์ง์„ ์‚ดํŽด๋ณด๋ฉด ์šฐ์„  cards[index].isMatched์ธ else ๊ตฌ๋ฌธ์˜ ๋กœ์ง์„ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

    if !cards[index].isMatched {
      if let matchIndex = indexOfOneAndOnlyFaceUpCard, matchIndex != index {
        if cards[matchIndex].identifier == cards[index].identifier {
          cards[matchIndex].isMatched = true
          cards[index].isMatched = true
        }
        cards[index].isFaceUp = true
        indexOfOneAndOnlyFaceUpCard = nil

    ๋ชจ๋“  ์กฐ๊ฑด์€ ๋งค์น˜๊ฐ€ ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ๋งŒ์„ ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”์†Œ๋“œ ์ „์ฒด๋ฅผ if ! ์œผ๋กœ ๊ฐ์‹ธ๊ฒŒ๋ฉ๋‹ˆ๋‹ค. ๋งค์น˜๋˜์ง€ ์•Š์•˜๋‹ค์˜ ์˜๋ฏธ๋Š” ์ด๋ฏธ ์ง์ด ์™„์„ฑ๋œ ์นด๋“œ๊ฐ€ ์•„๋‹ ๋•Œ์˜ ์กฐ๊ฑด์ž…๋‹ˆ๋‹ค. ๊ทธ ์•ˆ์— if let์˜ ์กฐ๊ฑด๋ฌธ์€ ์ด๋ฏธ ํ•œ์žฅ์˜ ์นด๋“œ๊ฐ€ ๋’ค์ง‘ํ˜€ ์žˆ๊ณ , ์ด๋ฏธ ๋’ค์ง‘ํ˜€์žˆ๋Š” ์นด๋“œ๋ฅผ ํด๋ฆญํ•œ ๊ฒƒ์ด ์•„๋‹Œ ์ƒˆ๋กœ์šด ์นด๋“œ๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

    ์ด๋ฏธ ๋’ค์ง‘ํ˜€์žˆ๋Š” ํ•œ์žฅ์˜ ์นด๋“œ๊ฐ€ ์ƒˆ๋กญ๊ฒŒ ๊ณ ๋ฅธ ์นด๋“œ์™€ ๊ฐ™์€ ์ด๋ชจ์ง€(์‹๋ณ„์ž)๋ผ๋ฉด ๋‘ ์นด๋“œ์˜ isMatched๋Š” true๋กœ ๋ฐ”๊พธ์–ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ณ ๋ฅธ ์นด๋“œ๋ฅผ ๋’ค์ง‘์–ด์ค€ ํ›„ ๋’ค์ง‘ํžŒ ์นด๋“œ๋ฅผ ์ดˆ๊ธฐํ™” ํ•ด์ค๋‹ˆ๋‹ค.

    else {
      // either no cards or 2 cards are face up
      for flipDownIndex in cards.indices {
        cards[flipDownIndex].isFaceUp = false
      }
      cards[index].isFaceUp = true
      indexOfOneAndOnlyFaceUpCard = index
    }

    ์ด ์ƒํ™ฉ์€ ์นด๋“œ๊ฐ€ ๋ชจ๋‘ ๋’ค์ง‘ํ˜€์žˆ๋Š” ์ƒํƒœ์—์„œ ์ฒ˜์Œ์œผ๋กœ ์นด๋“œ ํ•˜๋‚˜๋ฅผ ์˜คํ”ˆํ•˜๋Š” ์ƒํ™ฉ์ด๊ฑฐ๋‚˜ ์ด๋ฏธ 2์žฅ์ด ์˜คํ”ˆ๋˜์–ด ์žˆ๋Š” ์ƒํƒœ์—์„œ ๋‹ค์Œ ์•ก์…˜์œผ๋กœ ์นด๋“œ๋ฅผ ๋ˆ„๋ฅธ ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๋ชจ๋“  ์นด๋“œ๋ฅผ ๋‹ค์‹œ ๋’ค์ง‘์–ด์ฃผ๊ณ  ๊ณ ๋ฅธ ์นด๋“œ๋งŒ ๋‹ค์‹œ ๋’ค์ง‘์–ด์ฃผ๋Š” ๋กœ์ง์ด ๋ฉ๋‹ˆ๋‹ค.


Reference: Stanford CS193p by Paul Hegarty

๋ฐ˜์‘ํ˜•
๋Œ“๊ธ€
๋ฐ˜์‘ํ˜•
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
TAG more
ยซ   2024/11   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
๊ธ€ ๋ณด๊ด€ํ•จ