App Review Guidelines - Apple Developer
Sign in With Apple Overview
Revoke tokens | Apple Developer
Sign in with Apple - Human Interface Guidelines
์ด๋ฒ ๊ธ์์๋ Flutter์์ Apple ๋ก๊ทธ์ธ์ ๋ํ ๋ชจ๋ ๊ฒ์ ์์ธํ ์์๋ณด๋๋ก ํ๊ฒ ๋ค.
iOS ์ฑ์ ๋ฐฐํฌํ ๋, ๋ก๊ทธ์ธ ๋ฐฉ์์ผ๋ก ์์ ๋ก๊ทธ์ธ์ ์ ๊ณตํ๊ฒ ๋๋ฉด ๋ฌด์กฐ๊ฑด Apple ๋ก๊ทธ์ธ์ ์ ๊ณตํด์ผ ํ๋ค. ์ ํ์ ์ ์ฑ ์ด๋ค.
์๋ง์ ์ฑ์ ๋ฐฐํฌ ํด๋ดค์ง๋ง ์ฌ์ ํ ๊ฐ์ฅ ์ต์ง์ค๋ฌ์ด ์ ์ฑ ์ด๋ผ ์๊ฐํ๋ ๊ฒ์ด ๋ฐ๋ก Apple Login ์๋ฌดํ๋ค..
๊ฒฝํ์ ์ง๊ธ๊น์ง ๋ฐฐํฌํ ์๋น์ค๋ค์ ์์ ๋ก๊ทธ์ธ ๋น์จ์ ๋ณด๋ฉด ๊ตญ๋ด๋ ๊ฑฐ์ ์นด์นด์ค๊ณ , ๊ธ๋ก๋ฒ์์๋ ๋จ์ฐ ๊ตฌ๊ธ์ด ์๋์ ์ผ๋ก ๋น์ค์ด ๋์๊ธฐ ๋๋ฌธ์, ๋ก๊ทธ์ธ ๊ฐ๋ฐ์ ์ ํ ๋ก๊ทธ์ธ์ ๋งค์ฐ ๊ท์ฐฎ์ ์์ ์ผ ์ ๋ฐ์ ์๋ค.
ํ์ง๋ง ๋ฐฐํฌ๋ฅผ ์ํด์๋ ๊ฐ๋ฐ์๋ก์ ์ ์ฑ ๋ ์ง์ผ์ผํ๋ Apple Login์ ์ฌ์ฉํ ๋ก๊ทธ์ธ๊ณผ ์ธ์ฆ ์ ์ฐจ ๋ฐ ํํด์ ๊ฐ์ด๋๋ผ์ธ์ ๋ฐ๋ฅธ ์ฐ๋ ํด์ ์ ์ฐจ๊น์ง ์ ๋ฆฌํด ๋ณด๋๋ก ํ๊ฒ ๋ค.
*์์ ๋ก๊ทธ์ธ์ ์ ๊ณตํ์ง ์๋๋ค๋ฉด, Apple ๋ก๊ทธ์ธ์ ์๋ฌด๋ก ์ ๊ณตํ ํ์๋ ์์.
Apple ๋ก๊ทธ์ธ ์งํ ์ ๋ฐ๋์ ํ์ํ ์ค๋น๋ฌผ์ด ํ๋ ์๋ค. ๋ฐ๋ก Apple Developer Program ๋ฉค๋ฒ์ญ ๊ณ์ ์ด ํ์ํ๋ค. ์ฆ ์ ํ ๊ฐ๋ฐ์ ๊ณ์ ์ด ์์ด์ผ ํ๋ค.
๋ง์ผ ๊ฐ๋ฐ์ ๊ณ์ ์ด ์๋ ์ํ๋ผ๋ฉด, ์งํ์ด ๋ถ๊ฐ๋ฅํ๋ค.
์ง์์ ์ผ๋ก iOS๋ Apple ๊ด๋ จ ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ฐฐํฌํ ์์ ์ด๋ผ๋ฉด ๋น์ฐํ ๋ฉค๋ฒ์ญ ๊ตฌ๋ ์ด ํ์ ํ๊ฒ ์ง๋ง, ๋จ์ ํ์ต์ฉ์ผ๋ก ์ฌ์ฉํ๋ค๋ฉด ๋น์ฅ์ ๋ฉค๋ฒ์ญ ๊ตฌ๋ ์ ์ถ์ฒํ์ง๋ ์๊ณ ํ์ํ ๋์ ๊ตฌ๋ ์ ํด์ ์ฌ์ฉํด ๋ณด๋ ๊ฒ์ ์ถ์ฒ๋๋ฆฐ๋ค. ๋งค๋ 129,000์์ ์ง๋ถํด์ผ ํ๊ธฐ ๋๋ฌธ์ ๊ฒฐ์ฝ ์ ์ ๋์ ์๋๋ฉฐ, ์ค๊ฐ์ ๊ตฌ๋ ์ ์ค๋จํ๊ฒ ๋๋ฉด ๋ฐฐํฌ๋ ์ฑ์ ๋ชจ๋ ์ทจ์๋๋ค.
๋ณธ๊ฒฉ์ ์ผ๋ก Apple ๋ก๊ทธ์ธ์ ์งํํ๊ธฐ ์์ ํ์ํ ์ ์ฐจ๋ค์ ๋ํด์ ์ดํด๋ณด๋๋ก ํ์.
๋จผ์ ๊ฐ์์ ์ธ์ฆ, ๋ฐฑ์๋, Serverless ๋ฑ์ ํ๊ฒฝ๋ค์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ์ค์ ๋ฐฉ๋ฒ ๋ฐ ์ฌ์ฉ ๋ฐฉ๋ฒ๋ ์ผ๋ถ ๋ค๋ฅผ ์ ์๋๋ฐ ์ต๋ํ ๋ชจ๋ ๊ฒฝ์ฐ์ ๋ํด์ ๋ค๋ค๋ณผ ์์ ์ด๋ค.
์ฐธ๊ณ ๋ก Firebase๋ ๋ฐฑ์๋ ์์ด๋ Apple Login์ ๊ตฌ์ถํ๋ ๊ฒ์ ๊ฐ๋ฅํ๋ค.
Apple ๋ก๊ทธ์ธ์ OAuth 2.0 ๊ธฐ๋ฐ์ ์ธ์ฆ ๋ฐฉ์์ ์ฌ์ฉํ๊ณ ์๋ค. iOS, Mac OS, Web ๋ฑ์์ ์ฌ์ฉํ ์ ์๊ณ , Android ๋ํ ์ ํ์ ์ผ๋ก ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
์๋์์ ์์ธํ ๋ค๋ค๋ณด๊ฒ ์ง๋ง Apple ๋ก๊ทธ์ธ์์ ๋ฐ๋์ ๊ณ ๋ คํด์ผ ํ๋ ๋ถ๋ถ ์ค์ ํ๋๊ฐ Private Relay Email, ์ฌ์ฉ์ ์ ๋ณด ๋ฐ ํ ํฐ ๋ฌดํจํ์ ๊ดํ ๋ด์ฉ ์ด๋ค.
์ฌ์ฉ์๊ฐ ์ค์ ์ด๋ฉ์ผ์ ๊ณต์ ํ๊ธฐ๋ฅผ ์ํ์ง ์๋๋ค๋ฉด ์ค์ ์ด๋ฉ์ผ ์ฃผ์๋ ํ์ธํ ๋ฐฉ๋ฒ์ด ์๊ณ , ์ฌ์ฉ์ ์ ๋ณด์ ๋ํ ์ ๊ณต๋ ์ต์ด ๋ก๊ทธ์ธ์์๋ง ์ ๊ณต๋๊ณ ์ด ํ ๋ก๊ทธ์ธ์์๋ NULL์ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ์ด ๋ถ๋ถ์ ๊ณ ๋ คํด์ ์ค๊ณํ์ฌ์ผ ํ๋ค.
ํ ํฐ ๋ฌดํจํ๋ Apple์ ์ ์ฑ ์ด๊ธฐ ๋๋ฌธ์ ํ์๋ก ๊ตฌํํด์ผ ํ๋ ๊ธฐ๋ฅ์ด๋ค.
App Store Review Guidelines - 5.1.1 (v) Sign in with Apple
Apps that use Sign in with Apple must also support account deletion initiated from within the app.
Apple Login ์ ์ฐจ๋ ์๋์ ๊ฐ์ ์์๋ก ์งํํด ์ฃผ๋ฉด๋๋ค.
1. Apple ๋ก๊ทธ์ธ ํ์
2. ์ธ์ฆ ๋ฐ ID ํ ํฐ ๋ฐ๊ธ
3. Identity Token ๊ฒ์ฆ
4. ์ฌ์ฉ์ ์ ๋ณด ์ ๊ณต
5. ํ์ ํํด์ Apple ๊ณ์ ์ฐ๊ฒฐ ํด์
๊ฐ ๋จ๊ณ๋ณ ๋ฐฉ๋ฒ ๋ฐ ์ค๋ช ์ ๋ํด์๋ ์๋์์ ์์ธํ ๋ค๋ค๋ณด๋๋ก ํ๊ฒ ๋ค.
Apple ๋ก๊ทธ์ธ์ ์ํ ์ฌ์ ์ค์ ์ ์งํํด์ฃผ์.
Apple Developer > Certificates, Identifiers & Profiles์ ์ ์ํ๋๋ก ํ์.
๊ฐ๋ฐ์ ๊ณ์ ๊ณผ ๊ด๋ จ๋ ๊ธฐ๋ฅ์ Apple Developer์์ ์ ๊ณตํ๊ณ ์๊ณ , ์ฑ๊ณผ ๊ด๋ จ๋ ๊ธฐ๋ฅ์ Store Connect ์์ ์ ๊ณตํ๊ณ ์๋ค.
๋๋ฒจ๋กํผ์์๋ ์ฑ์ ์๋ณ์์ Sign in with Apple ๊ธฐ๋ฅ์ ํ์ฑํํ๊ณ ํ์์ ๋ฐ๋ผ ํค๋ฅผ ๋ฑ๋กํ๋ ์ ์ฐจ๋ฅผ ์งํํด์ฃผ๋ฉด ๋๋ค.
๋จผ์ ์๋ณ์๋ฅผ ์์ง ๋ฑ๋กํ์ง ์์ ๊ฒฝ์ฐ๋ผ๋ฉด Identifiers ํญ์ผ๋ก ์ด๋ํด์ ์ถ๊ฐ๋ฅผ ํด์ฃผ์๋ฉด ๋๋ค.
Apple Developer์ ์ฒ์ ๋ฐฉ๋ฌธํ์ ๋ถ๋ค๋ ๊ณ์ค ์ ์์ผ๋ ์ต๋ํ ์์ธํ๊ฒ ์ค๋ช ํ๋๋ก ํ๊ฒ ๋ค. ๊ธฐ์กด ์๋ณ์๋ฅผ ์ฌ์ฉํ๋ ๋ถ๋ค์ ์๋์ "Sign in with Apple" ํ์ฑํ ๋จ๊ณ๋ถํฐ ์งํํ์๋ฉด ๋๋ค.
์๋ณ์๋ฅผ ์ถ๊ฐํ ๋์ ์ฌ๋ฌ๊ฐ์ง ์๋ณ์๊ฐ ์๋๋ฐ, ์ฑ๋ง ํ๊ฒํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ App IDs๋ก ์งํ์ ํ์.
![]() |
![]() |
App๊ณผ App Clip ๋ ๊ฐ์ง๋ฅผ ์ ํํ ์ ์๋๋ฐ, ๊ฐ๋จํ๊ฒ ์ค๋ช
ํ๋ฉด App์ ์ผ๋ฐ์ ์ผ๋ก App Store๋ฅผ ํตํด์ ์ ๊ณตํ๋ ์์ ํ ์ ํ๋ฆฌ์ผ์ด์
์ด๊ณ App Clip์ ์ฌ์ฉ์๊ฐ App Store์์ ์ ์ฒด ์ฑ์ ์ค์นํ์ง ์์๋ ํน์ ๊ธฐ๋ฅ์ ๋น ๋ฅด๊ฒ ์ ๊ณตํ๊ธฐ ์ํ ๊ฒฝ๋ํ๋ ๋ฏธ๋ ์ฑ์ด๋ผ๊ณ ๋ณด์๋ฉด ๋๋ค.
๊ธฐํ๊ฐ ๋๋ค๋ฉด App Clip์ ๋ํด์ ์ถํ์ ๋ค๋ค๋ด๋ ์ข์ ๊ฒ ๊ฐ๋ค.
App ํ์ ์ ์ ํํด์ฃผ๋๋ก ํ์.
Bundle ID๋ฅผ ์ ๋ ฅํด ์ฃผ๋ฉด ๋๋๋ฐ, Bundle ID๋ ํ๋ก์ ํธ์ XCode๋ฅผ ์ด๋ฉด Bundle Identifier์์ ํ์ธํ ์ ์๋ค.
๋ง์ผ ๊ฐ๋ฐ์ ๊ณ์ ์ ์ฌ๋ฌ ์ฑ์ ๋ฐฐํฌํ ๊ณํ์ด๋ผ๋ฉด Description ํ๋์ ํ์คํ ์ ๋ณด๋ฅผ ๊ธฐ์
ํ์ฌ์ผ ํ๋ค.
์ฌ๋ฌ ์ฑ์ ๋ฐฐํฌํ๊ฒ ๋๋๋ผ๋ ๊ฐ๋ฐ์ ๊ณ์ ์ด ํ ๊ฐ๋ผ๋ฉด ๋ชจ๋ ์๋ณ์๊ฐ ํด๋น ํญ์์ ๊ด๋ฆฌ๋๊ธฐ ๋๋ฌธ์, ์๋ณ์ ๊ด๋ฆฌ๋ฅผ ์ํด์ ์ ์ ํ ๋ค์ด๋ฐ์ ๋ฃ์ด์ผ ๊ด๋ฆฌ๊ฐ ์์ํด ์ง๋ค.
์ด์ ์ ๋ค๋ฅธ ์๋ณ์๋ฅผ ์ญ์ ํ๋ ๋ฐ๋์ ๊ณคํน์ ์น๋ค๋ ๊ฒฝํ์ด ์์๋ค...
๋จ ํ๋์ ์ฑ๋ง ๋ฐฐํฌํ ๊ฒฝ์ฐ๋ผ๋ฉด ๊ทธ๋ฅ ํ๋ก์ ํธ๋ช ์ ๋ฃ์ด์ฃผ๋๊ฒ ์ผ๋ฐ์ ์ด๋ค.
์๋ณ์์ Sign in with Apple ๊ธฐ๋ฅ์ ํ์ฑํ ํด์ฃผ์ !
๊ธฐ์กด ์๋ณ์๋ฅผ ์ฌ์ฉํ์๋ ๋ถ๋ค์ ์๋ณ์๋ฅผ ํญํ๋ฉด ํธ์ง์ ํ ์ ์๋ค.
Capabilities ํญ์์ ์๋๋ก ์คํฌ๋กคํ์ฌ Sign in with Apple์ ์ ํํด ์ฃผ๋๋ก ํ์.
์ด์ Apple ๋ก๊ทธ์ธ์ด ํ์ฑํ ๋๊ฒ์ด๋ค.
![]() |
![]() |
Edit ๋ฒํผ์ด ๋ณด์ด๋๋ฐ, Edit๊ณผ ๊ด๋ จ๋์๋ ํ์ ํญ๋ชฉ์ ์๋๋ ํ์์ ๋ฐ๋ผ ์ ์ฐจ๋ฅผ ์งํํ์๋ฉด ๋๋ค.
App ID์ ๊ด๋ จ๋ ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ์์ญ์ผ๋ก, ์ฌ๋ฌ ํ๋ซํผ์์ ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ณต์ ํ๊ณ ์ ํ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
Primary App ID๋ ์ฌ๋ฌ ํ๋ซํผ ๊ฐ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ณต์ ํ ์ ์๋๋ก ์ง์ํ๋ ์ฑ ๊ทธ๋ฃน์ ์ฃผ์ ID ์ญํ ์ ํ๋ค.
์ฌ์ฉ์๊ฐ ํ ๋ฒ ๋์ํ๋ฉด, ๊ทธ๋ฃน ๋ด ๋ชจ๋ ์ฑ์์ Sign in with Apple ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ณต์ ํ ์ ์๋ค.
์ดํด๊ฐ ์ ์ ๋์๋ ๋ถ๋ค์ iOS ๊ฐ๋ฐ ์ ์ฌ์ฉํ๋ App Groups์ ๋ํด ์์๋ณด์๋ฉด ๊ธ๋ฐฉ ์ดํดํ ์ ์์ ๊ฒ์ด๋ค.
App Group์ ์ฌ๋ฌ ์ฑ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํ ์ ์๋๋ก ์ค์ ํ๋ ๊ธฐ๋ฅ์ผ๋ก, Primary App ID๋ ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ณต์ ํ๋ค๋ ์ ์์ ์ ์ฌํ๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ์ ํ๋ ์ต์
์ ํ์ฌ ๋ฑ๋ก ์ค์ธ App ID๋ฅผ ์๋ก์ด Primary App ID๋ก ์ค์ ํ๋ ๊ฒ์ด๋ค.
๋ง์ฝ "Group with an existing ..." ์ต์
์ ์ ํํ๋ฉด, ๊ธฐ์กด์ ๋ฑ๋ก๋ Primary App ID์ ๊ทธ๋ฃนํ๋์ด ๋์ผํ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
ํด๋น ์น์ ์ ๋ฐฑ์๋ ํ๊ฒฝ์ด๋ Firebase Authentication ๋ฑ์ ์ธ์ฆ ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ง ์ค์ ํ ์ ์๋ค.
Apple์ด ์ฑ์์ ์ค์ํ ๊ณ์ ๊ด๋ จ ๋ณ๊ฒฝ ์ฌํญ์ ๊ฐ์งํ๋ฉด ํด๋น URL๋ก ์๋ฆผ์ ๋ณด๋ด์ฃผ๋ ๊ธฐ๋ฅ์ธ๋ฐ, ๋ฑ๋กํ URL์ Apple์ด ์๋ฒ ์์ฒญ์ ์ ์กํ ์นํ (Webhook)์ ์๋ํฌ์ธํธ ์ญํ ์ ํ๊ฒ ๋๋ค.
์๋ฆผ์ด ๋ฐ์ํ๋ ์ฃผ์ ์ด๋ฒคํธ๋ก๋ Apple ID์ ์ฑ์ ์ฐ๊ฒฐ์ ํด์ ํ ๊ฒฝ์ฐ(Revoke), Apple ID๊ฐ ์ญ์ ๋ ๊ฒฝ์ฐ, Private Relay Email์ด ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ์ด๋ค.
์ค์ URL์ ๋ฐ๋์ HTTPS๋ฅผ ์ฌ์ฉํด์ผ ํ๋ฉฐ, TLS 1.2 ์ด์์ ์ง์ํด์ผ ํ๋ค.
์์ธํ ๋ด์ฉ์ ๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
๋ง์ง๋ง์ผ๋ก ์ ๋ ฅํ ์ ๋ณด๋ฅผ ํ์ธ ํ ์์ฑํด์ฃผ๋ฉด ์๋ณ์๊ฐ ์ถ๊ฐ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
![]() |
![]() |
Flavors ๋ฑ์ ๋น๋ ๋ณํ์ ๊ตฌ์ถํ ํ๊ฒฝ์ด๋ผ๋ฉด ๋ณํ๋ ์๋ณ์๋ฅผ ์ถ๊ฐ๋ก ํด๋น Identifiers์ ์ถ๊ฐํด์ฃผ๋ฉด ๋ชจ๋ ํ๊ฒฝ์์ ์ํํ Apple Login์ ์ฌ์ฉํ ์ ์๋ค.
Firebase ํ๋ก์ ํธ ์์ฑ๋ถํฐ ์ฐ๋๊น์ง...
Firebase Authentication์ ์ฌ์ฉํ๋ ๋ถ๋ค์ ์ถ๊ฐ์ ์ธ ์ ์ฐจ๊ฐ ํ์ํ๋ค.
์ฐ์ Firebase ์ฐ๋์ด ๋์ด์์ง ์๋ค๋ฉด ์ฐ๋ ํ์ ์ ์ฐจ๋ฅผ ์ํํ์๋ฉด ๋๋ค.
Authentication์ ์ ํํ ํ์ ๋ก๊ทธ์ธ ๋ฐฉ๋ฒ ํญ์ผ๋ก ์ด๋ํ์ฌ ์ ์ ๊ณต์ ์ฒด๋ฅผ ์ถ๊ฐํด์ฃผ์. ์ ๊ณต์ฒ๋ก Apple์ ์ ํํด ํ์ฑํ ํด์ฃผ๋๋ก ํ์.
์๋จ์ ์ฌ์ฉ์ค์ ์ ํ์ฑํํ๊ณ ํ๋จ์ ๋๋ฉ์ธ์ ๋ณต์ฌํด์ ์ ์ฅ์ ๋๋ฌ์ฃผ์.
์ฌ๊ธฐ์ ํ๋จ์ ์์ฑ๋ ๋๋ฉ์ธ์ด ๋ฐ๋ก ์์์ ์ดํด๋ณธ Server-to-Server Notification Endpoint์ URL์ด ๋๋ค.
์์์๋ ์ค๋ช ํ๋ฏ์ด ํ์ ์ฌํญ์ ์๋๊ณ , ํด๋น ๋๋ฉ์ธ์ Apple Developer์ ๋ฑ๋กํ์ง ์์๋ Apple Login ๊ธฐ๋ฅ ์์ฒด๋ฅผ ์ฌ์ฉํ๋๋ฐ๋ ๋ฌธ์ ๊ฐ ์์ง๋ง ๊ณ์ ๋ณํ์ ๋ํ ์๋ฆผ ๊ตฌ๋ ์ ๋ฐ์ง ๋ชปํ๋ ์ถ๊ฐํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค.
Identifiers ํญ์์ ์์ฑํ ์๋ณ์๋ฅผ ์ ํ ํ ํ์ฑํํ Sign in with Apple ๊ธฐ๋ฅ์ Edit์ผ๋ก ์ ๊ทผํ๋ฉด ํด๋น ๋๋ฉ์ธ์ ๋ฃ์ด ์ค ์ ์๋ค.
![]() |
![]() |
![]() |
Apple Login ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ ์ํด ์ฌ๋ฌ ๋ธ๋ก๊ทธ๋ ๊ด๋ จ ๋ํ๋จผํธ๋ฅผ ๋ณด์ ๋ถ๋ค ์ค์์๋ ๋์์๋ ๋ด์ฉ๋ง๋ค ์ผ๋ถ ์ค์ ์ ๋ค๋ฅด๊ฒ ์ค๋ช ํ๊ณ ์์ด ํท๊ฐ๋ฆฌ์ ๋ถ๋ถ๋ค์ด ์์ ๊ฒ๋๋ค.
์ค์ ์ด ์กฐ๊ธ์ฉ ๋ค๋ฅธ ์ด์ ๋ ์๋ง๋ Android๋ Web์ ํ์ํ ์ค์ ์ด ์ถ๊ฐ๋ก ํ์ํ๊ฑฐ๋ ํด์ ๊ทธ๋ฐ๊ฒ๋๋ค.
Firebase Authentication ์ค์ ๊ณผ ๊ด๋ จํด์๋ ์ด๋ค ID๋ก ์๋ณ์๋ฅผ ์์ฑํ์๊ณ , ์ด๋ค ์๋ฒ ํ๊ฒฝ์ ๊ตฌ์ถํด์ ์ฌ์ฉ ํ๋์ง์ ๋ฐ๋ผ์ ์กฐ๊ธ์ฉ ์ค์ ์ด ๋ค๋ฅด๊ฒ ๋๋ ๊ฒ์ด๋ค.
๋ง์ผ iOS์์๋ง Apple Login ๊ธฐ๋ฅ์ ์ฌ์ฉ ํ์ ๋ค๋ฉด ์ฌ๊ธฐ๊น์ง ์ค์ ํ์๋ฉด ๋๊ณ , ๋๋จธ์ง ์ค์ ๋ค์ ๋ถํ์ํ ์ค์ ์ด ๋ ์๋ ์์ต๋๋ค.
ํ์ ์๋ ์ค์ ๋ค์ด ์ถ๊ฐ๋์ด ์์ด๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ณ ์ด์ํ๋๋ฐ๋ ์ ํ ๋ฌธ์ ๊ฐ ์๋ ๊ฒ๋ ๋ง์ง๋ง ์๊ฐ์ด ํ๋ฆ์ ๋ฐ๋ผ ์ฌ๋ฌ ๊ฐ๋ฐ์์ ์์ ํ๋ฉด์ ์ด์์ด ๋๋ค๋ณด๋ฉด ์ ์ง๋ณด์๋ ์ ์ ์ด๋ ค์์ง๊ธฐ ๋ง๋ จ์ด๋ผ ์ต๋ํ ํ์ํ ๋ถ๋ถ๋ง ์ค์ ํ ์ ์๋๋ก ํ๋ ํธ์
๋๋ค.
๊ทธ๋๋ ๊ถ๊ธํ์ ๋ถ๋ถ๋ค์ด ์์ผ๋ ๋ช ๊ฐ์ง ์ค์ ์ ๋ํด์ ๊ฐ๋จํ๊ฒ๋ง ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๋จผ์ App IDs์ Services IDs๋ฅผ ์ด๋ค ํ์ ์์ ์ฌ์ฉํด์ผ ํ๋์ง์ ๋ํด์ ์์๋ณด๋ฉด, App ID๋ ์ผ๋ฐ์ ์ผ๋ก iOS์ ๋ค์ดํฐ๋ธ ์ฑ์์ ์ฌ์ฉํ๋ ๊ณ ์ ์๋ณ์๋ผ๊ณ ์๊ฐํ์๋ฉด ๋ฉ๋๋ค. ๋ฐ๋ฉด Services ID๋ ์น ์๋น์ค์ ํตํฉ์ด ํ์ํ๊ฑฐ๋ OAuth ๊ธฐ๋ฐ์ ์ธ์ฆ ํ๋ฆ์ ๊ตฌํํ๊ณ ์ฑ๊ณผ ์น๊ฐ์ ๋ฆฌ๋ค์ด๋ ์ ์ ์ฒ๋ฆฌํ๊ธฐ ์ํ Apple ๋ก๊ทธ์ธ์ ๊ตฌํํ ๋์ ์ฌ์ฉํ ์ ์๋ ์๋ณ์ ์ ๋๋ค.
Apple Login์์๋ Android์์ OAuth ๊ธฐ๋ฐ์ ์ธ์ฆ์ ๊ตฌํํ๊ณ ์ ํ๋ค๋ฉด Services ID๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๊ทธ ๋ค์์ Key๋ฅผ ์์ฑํ๋ ๋ถ๋ถ์ธ๋ฐ, Apple๊ณผ์ ๋ณด์ ํต์ ์ ์ํด ์ฌ์ฉํ๋ ์ํธํ ํค๋ฅผ ์์ฑํ๋ ๊ฒ์ผ๋ก, Apple Login์์๋ ์ธ์ฆ ๋ฐ JWT ์๋ช ์ด ํ์ํ ๊ฒฝ์ฐ์ ์์ฑํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
์ด์ Flutter ์ฝ๋๋ฅผ ์ฌ์ฉํด ์ ํ ๋ก๊ทธ์ธ์ ๊ตฌํํด ๋ณด๋๋ก ํ์.
๋จผ์ Apple Login์ ํ๋ซํผ ์ฑ๋์ ํตํด ๋ค์ดํฐ๋ธ๋ก ์ง์ ๊ตฌํํ ์ ์์ง๋ง, ์ฌ๊ธฐ์๋ ํจํค์ง๋ฅผ ์ฌ์ฉํด ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํด ์ฃผ๋๋ก ํ๊ฒ ๋ค.
dependencies:
sign_in_with_apple: ^6.1.4
Firebase ์ฌ์ฉ์ firebase_auth ํจํค์ง๋ฅผ ์ถ๊ฐํ๋๋ก ํ์.
Apple ๋ก๊ทธ์ธ์ ์ํ Developer ์ค์ ์ ์ด์ด์ XCode์์๋ Sign in with Apple ๊ธฐ๋ฅ์ ํ์ฑํ ํด์ฃผ์.
Signing & Capabilities ํญ์ผ๋ก ์ด๋ํ์ฌ Capabilities ์ถ๊ฐ๋ฅผ ๋๋ฌ Sign in with Apple์ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค.
iOS ๊ฐ๋ฐ์ ๊ด๋ จํด์ ์ต์ํ์ง ์์ผ์ ๋ถ๋ค์ ์ ๊ฑธ ์ ์ถ๊ฐํ๋์ง ๊ถ๊ธํ์ค ์๋ ์๋ค. ์ถ๊ฐํ๋ฉด ๊ทธ๋ฅ ๋ชฉ๋ก์๋ง ์ถ๊ฐ๋์ง ๋ค๋ฅธ ๊ธฐ๋ฅ์ด ์๊ฑฐ๋ ๊ทธ๋ ์ง ์๊ธฐ ๋๋ฌธ์ผ ๊ฒ์ด๋ค.
Capabilities๋ ์ฃผ๋ก Apple์ ์์คํ ๊ณผ ๊ด๋ จ๋ ๊ธฐ๋ฅ๋ค์ ํ์ฑํ๊ฐ ํ์ํ ๋ ์ถ๊ฐํ๊ฒ ๋๋ ๊ฒ์ผ๋ก ํด๋น ๊ธฐ๋ฅ์ด ํ์ฑํ ๋์ง ์์ ์ํ๋ก ๋ฐฐํฌ๊ฐ ์ด๋ค์ง๋ฉด ๊ธฐ๋ฅ์ด ์ ์์ ์ผ๋ก ์๋ํ์ง ์๋๋ค.
Apple ์์คํ (์ฌ๊ธฐ์๋ iOS) API๋ ์ฑ์ Capabilities ์ค์ ์ ๋ฐํ์ผ๋ก ์์คํ ์ ๊ธฐ๋ฅ์ ํ์ฑํํ๊ธฐ ๋๋ฌธ์ XCode์์๋ Capabilities ์ถ๊ฐ๊ฐ ๋ฐ๋์ ํ์ํ ๊ฒ์ด๋ค.
๋ํ์ ์ธ Capabilities ๊ธฐ๋ฅ๋ค๋ก๋ ์ฐ๋ฆฌ๊ฐ ์ ์๊ณ ์๋ ์์คํ API์ธ Notifications, Apple Pay, iCloud, HealthKit, App Groups ๋ฑ์ด ์๋ค.
์ ํ ๋ก๊ทธ์ธ ์ฌ์ฉ์์๋ ๊น๋ค๋ก์ด ์ ํ์ ๋์์ธ ์ ์ฑ ์ ์ค์ํ์ฌ์ผ ํ๋ค. ๋น์ฐํ ์ค์ํ์ง ์๊ณ ์ ๋ฉ๋๋ก UI๋ฅผ ๋ฐ์ํ๊ฒ ๋๋ฉด Apple ์ฌ์ฌ ๊ฑฐ์ ๊ฐ๋ฅ์ฑ์ด ๋๋ค.
Sign in with Apple - Human Interface Guidelines ๊ธฐ์ค์ ํ์ธํ ํ UI์ ๋ฐ์ํ์ฌ ๋ก๊ทธ์ธ ๋ฒํผ์ ๋ง๋ค๋๋ก ํ์.
์๋ ์ฝ๋๋ฅผ ์ ๋ ฅํ๋ฉด ์ ํ ๋ก๊ทธ์ธ์ ์์ฒญํ๊ฒ ๋๋ค. scopes ํ๋ผ๋ฏธํฐ๋ ํ์ ๊ฐ์ผ๋ก ์ํ๋ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํด์ ์์ฒญํ ์ ์๋๋ฐ, ์ํ๋ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด ๋น ๋ฐฐ์ด๋ก ์์ฒญํด๋ ์๊ด ์๋ค.
await SignInWithApple.getAppleIDCredential(scopes: [
// AppleIDAuthorizationScopes.email,
// AppleIDAuthorizationScopes.fullName,
]);
None | [ email ] | [ fullName ] | [ email, fullName ] |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
์ ํ ๋ก๊ทธ์ธ์ผ๋ก ์ ๊ณต ๋ฐ์ ์ ์๋ ๋ฐ์ดํฐ๋ email, fullName ์ด๊ฒ ๋์ด๋ค...
์ ์ฑ ์ ๊ฐ์ธ์ ๋ณด๋ฅผ ์๊ฒฉํ๊ฒ ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์ ์ ํ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๊ณต ๋ฐ์ ์ ์๋๋ฐ, Email์ธ ๊ฒฝ์ฐ๋ ์ฌ์ง์ด Hide My Email(์ด๋ฉ์ผ ๊ฐ๋ฆฌ๊ธฐ) ์ต์ ์ ์ ํํ ์ ์์ด ์ค์ ์ด๋ฉ์ผ ์ฃผ์๊ฐ ์๋ ๊ฐ์ ์ด๋ฉ์ผ ์ฃผ์๋ฅผ ์ป๊ฒ ๋ ์๋ ์๋ค.
๋๋ถ๋ถ์ ์์ ์ ๊ณต์ ์ฒด๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ ํ๋กํ ์ด๋ฏธ์ง ์กฐ์ฐจ ๊ฐ์ ธ์ฌ ์๊ฐ ์๋ค.
์ ํ ๋ก๊ทธ์ธ์ด ๋ค๋ฅธ ์์ ๋ก๊ทธ์ธ์ ๋นํด ๊น๋ค๋ก์ด ๋ถ๋ถ ์ค์ ํ๋๋ ๋ฐ๋ก ์ต์ด ๋ก๊ทธ์ธ ์์ฒญ์์ ํ ๋ฒ๋ง email, fullName๋ฑ์ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณต ํ๋ค๋ ๊ฒ์ด๋ค..
์ต์ด ํ ๋ฒ๋ง ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ ํ ๋ถํฐ๋ null์ ๋ฐํํ๋ค. ์ ๋ง null์ ๋ฐํํ๋์ง credential์ ์ถ๋ ฅํด ๋ณด๋๋ก ํ์.
์ต์ด ์์ฒญ์์๋ ์ด๋ฆ๊ณผ ๋ฉ์ผ์ ๋ณด๋ฅผ ์ ์์ ์ผ๋ก ๋ฐํํ์ง๋ง ๊ทธ ์ด ํ๋ถํฐ๋ null๋ก๋ง ๋ฐํํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ค์ ๋ก ์ ํ๋ก๊ทธ์ธ ํ๋ฉด์์๋ ์ต์ด ๋ก๊ทธ์ธ๊ณผ ์ด ํ ๋ก๊ทธ์ธ์ ํ๋ฉด๋ ๋ค๋ฅด๊ฒ ๋ํ๋๋ค.
์ด๋ป๊ฒ ์ฌ์ฉ์๋ฅผ ์๋ณํ ์ ์์๊น ? ์ ํ์ ์ฌ์ฉ์ ๊ณ ์ ์๋ณ์๋ก userIdentifier๋ฅผ ์ ๊ณตํ๊ณ ์์ด์ userIdentifier๋ฅผ ํ์ฉํ์ฌ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉ์ ๊ฒ์ฆ์ ์ฌ์ฉํ๋ฉด ๋๋ค.
![]() |
![]() |
์ ํ์ด ์ ๊ณตํ๋ Email์ ๋ํด์ ์ข ๋ ์์ธํ ์์๋ณด์.
์ ํ ๋ก๊ทธ์ธ์์ ์ ํํ ์ ์๋ ์ต์ ์ Share My Email, Hide My Email ์ด๋ ๊ฒ 2๊ฐ์ง ์ต์ ์ด ์๋ค.
Share My Email(์ด๋ฉ์ผ ๊ณต์ )์ ์ค์ ์ด๋ฉ์ผ ์ฃผ์๋ก ๋ก๊ทธ์ธํ์ฌ ์ผ๋ฐ์ ์ธ ์์ ๋ก๊ทธ์ธ๊ณผ ๋ค๋ฅธ๊ฒ ์์ง๋ง Hide My Email(์ด๋ฉ์ผ ๊ฐ๋ฆฌ๊ธฐ)์ ๊ฐ์์ ์ด๋ฉ์ผ ์ฃผ์์ธ ๊ฒ์ด๋ค.
์ด๋ฉ์ผ๊ณผ ๊ด๋ จ๋ ์ต์ ์ ๊ฐ๋ฐ์๋ค์ด ์ ํ์ ์ผ๋ก ์์ฒญํ ์ ์๊ฒ ๋์์ผ๋ฉฐ, ์ฑ ์ฌ์ฉ์ ์์ด์ ์ด๋ฉ์ผ ๊ฐ๋ฆฌ๊ธฐ๋ฅผ ์ ํํ ์ฌ์ฉ์๋ฅผ ์ฑ ๋ด์์ ์ง์ ์ ํ์ฉํ์ง ์๊ฑฐ๋ ์๋ฌด์ ์ผ๋ก ์ค์ ์ด๋ฉ์ผ์ ์ ๋ ฅํ๋๋ก ํด์๋ ์๋๋ค๊ณ ํ๋ค (์๋ง๋ ๋ฆฌ์ ์ฌ์ ์ธ ๋ฏ...).
Hide My Email ์ต์ ์ผ๋ก ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ํํด์ ์ ํ์ ๊ฐ์ ์ด๋ฉ์ผ ์ฃผ์๋ก Private Reley Email์ ์์ฑํด ์ ๊ณตํ๊ฒ ๋๋ค.
Private Reley Email์ ์ฑ๋ง๋ค ๊ณ ์ ํ๊ฒ ์์ฑ์ด ๋๊ณ , ํด๋น ์ด๋ฉ์ผ๋ก ๋ฉ์ผ์ ๋ณด๋ด๋ฉด, ์ค์ ์ฌ์ฉ์์ ์ด๋ฉ์ผ ์ฃผ์๋ก ๋ฉ์ผ์ ์ ์กํด์ฃผ๋ Forwarding ๊ธฐ๋ฅ์ ์ ๊ณตํด์ฃผ๊ณ ์๋ค.
ํ์ง๋ง, ๊ฐ๋ฐ์๊ฐ ๊ณ ๋ คํด์ผ ํ๋ ๋ช ๊ฐ์ง ์ฌ์๋ ์๋๋ฐ ๋ฐ๋ก ๋ก๊ทธ์ธํ ์ฌ์ฉ์๊ฐ ์๋์ผ๋ก ๋ฉ์ผ์ ์ฐ๊ฒฐ์ ํด์ ํ ์ ์๊ธฐ ๋๋ฌธ์ ํญ์ ์ด๋ฉ์ผ ์ธ์ ์ฐ๊ฒฐ ์๋จ์ ๋ง๋ค์ด์ผ ํ๋ค๋ ๊ฒ์ด๋ค. ๋ง์ผ ์๋์ผ๋ก ์ฐ๊ฒฐ์ ํด์ ํ์ง๋ง ์๋๋ค๋ฉด ์๊ตฌ์ ์ผ๋ก ๋ง๋ฃ๋์ง ์๊ณ ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
*Private Reley Email๊ณผ ๊ด๋ จํด์๋ ์ถํ ์์ธํ ๋ด์ฉ์ผ๋ก ์์ฑํด ๋ณด๋๋ก ํ๊ฒ ๋ค.
์ฑ์์ ์ ์์ ์ผ๋ก ์ ํ ๋ก๊ทธ์ธ์ด ์ํ๋๊ณ ๋๋ฉด, ๊ฐ ํ๊ฒฝ์ ๋ฐ๋ฅธ ๋ค์ ์คํ
์ ์งํํด ์ฃผ๋ฉด ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ์ ํ ๋ก๊ทธ์ธ๊ณผ ๊ฐ์ ์์
๋ก๊ทธ์ธ์ ํ๋์ ์ธ์ฆ ์๋จ์ผ ๋ฟ์ด๊ธฐ ๋๋ฌธ์, ๊ณ ์ ID๋ฅผ ์ฌ์ฉํด ์ฑ ๋ด๋ถ์์ ๋ณ๋๋ก ์ธ์ฆ์ ์งํํ๊ฒ ๋ ๊ฒ์ธ๋ฐ, ํด๋น ์ ์ฐจ๋ ํ์ฌ ์๋น์ค์ ๋ด๋ถ ์ ์ฐจ๋๋ก ํด์ฃผ๋ฉด ๋๋ค.
์ถ๊ฐ์ ์ผ๋ก ์๋ฒ์์ Apple ID ๋ํ ๊ฒ์ฆ์ ๊ฑฐ์ณ์ผ ํ๋ค๋ฉด, ์ฑ์์ ๋ก๊ทธ์ธ ์ฑ๊ณต ํ ๋ฐํ๋๋ ๋ฐ์ดํฐ ์ค authorizationCode, identityToken์ ์๋ฒ์ ์ ๋ฌํ์ฌ ์ ํ๋ก ๋ถํฐ ํ ํฐ ๊ฒ์ฆ์ ์์ฒญ ํ๋ ๋ฑ์ ์ ์ฐจ๋ฅผ ์ํํ ์ ์๋ค.
sign_in_with_apple ํจํค์ง ์ฌ์ฉ์ ์์ธ ์ฒ๋ฆฌ์ ๋ํด์ ๊ฐ๋จํ๊ฒ ์ดํด๋ณด์.
Exception ์ฒ๋ฆฌ์ SignInWithAppleAuthorizationException์ ์บ์นํด์ ์๋ฌ๋ฅผ ํ์ธํ ์ ์๋ค. AuthorizationErrorCode๋ฅผ ๋ฐํํ๋๋ฐ, ์์ธ ํ์ ์ 6๊ฐ์ง๊ฐ ์๋ค.
enum AuthorizationErrorCode {
canceled, failed, invalidResponse, notHandled, notInteractive, unknown
}
์์ธ ์ผ์ด์ค์ ๋ํ ์ค๋ช ์ ํด๋น ํจํค์ง ํ์ผ์ ์ฐธ๊ณ ํ์๋ฉด ๋๋ค.
Firebase Authentication์์ ์ฌ์ฉํ๋ ์์ ๋ก๊ทธ์ธ ์ธ์ฆ ๋ฐฉ์๊ณผ ๋์ผํ๊ฒ ์ ํ ๋ก๊ทธ์ธ์ ์ฐ๊ฒฐํ๊ณ ๊ด๋ฆฌํ ์ ์๋ค.
์ ํ ๋ก๊ทธ์ธ ์ธ์ฆ ์ฑ๊ณต์ ๋ฐํ๋๋ identityToken
๊ณผ authorizationCode๋ฅผ Authentication์ OAuthProvider๋ก ์ ์กํด ์ฌ์ฉ์ ์ธ์ฆ์ ๋ง๋ฌด๋ฆฌ ํ ์ ์๋ค. ์ด ๋, provider๋ก apple.com์ ์ฌ์ฉํด ์ฃผ๋ฉด ๋๋ค.
final OAuthCredential authCredential = OAuthProvider("apple.com").credential(
idToken: credential.identityToken,
accessToken: credential.authorizationCode,
);
์์ฑ๋ OAuthCredential์ผ๋ก ๋ก๊ทธ์ธ์ ์์ฒญํด UserCredential์ ์ ์์ ์ผ๋ก ์์ฑํ ์ ์๋ค.
final UserCredential user = await FirebaseAuth.instance.signInWithCredential(authCredential);
Future<void> signInWithApple() async {
try {
final AuthorizationCredentialAppleID credential =
await SignInWithApple.getAppleIDCredential(scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
]);
print(credential.userIdentifier);
// [Optional] Firebase Authentication ์ฐ๋ ์ ์ ์ฐจ
final OAuthCredential authCredential = OAuthProvider("apple.com").credential(
idToken: credential.identityToken,
accessToken: credential.authorizationCode,
);
final UserCredential user =
await FirebaseAuth.instance.signInWithCredential(authCredential);
} on SignInWithAppleAuthorizationException catch (e) {
// Handling errors on failure
} catch (_) {
// Handling other errors
}
}
Sign in with Apple ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ธฐ ์ํ ๋๋ฒจ๋กํผ ์ธํ , ์ ํ ๋ก๊ทธ์ธ ์์ฒญ, ๋ง์ง๋ง์ผ๋ก ์ฌ์ฉ์ ์ธ์ฆ๊น์ง ์๋ฃ๋ฅผ ํ๋ค.
๊ณผ์ฐ ๋ชจ๋ ์ ์ฐจ๊ฐ ๋๋๊ฑธ๊น ?
์๋๋ค. Apple ๋ก๊ทธ์ธ์ ์์ฒญํ๋ ๊ฒ ๋งํผ์ด๋ ์ค์ํ ์์ ์ด ๋จ์์๋ค. ๋ฐ๋ก ํ ํฐ ๋ฌดํจํ(Revoke Token)๊ธฐ๋ฅ์ด๋ค.
์ฌ์ค ์ ํ ๋ก๊ทธ์ธ๊ณผ ๊ด๋ จํด์๋ ๊ด๋ จ ๋ด์ฉ์ด๋ ์ ๋ณด๋ฅผ ์ฝ๊ฒ ์ฐพ์๋ณผ ์ ์๊ณ , ์ด๋ฏธ ๋ง์ ๊ฐ๋ฐ์ ๋ถ๋ค์ด ์์ธํ๊ฒ ๊ธ์ ์์ฑํด์ฃผ์ ์ ๋ณ๋๋ก ๊ธ์ ์์ฑํ ๊ณํ์ ์์์๋ค. ํ์ง๋ง ๋ช ๋ ๋ง์ ์ ๊ท ์๋น์ค ์ถ์ ๊ธฐํ๊ฐ ์์ด์ ์ ํ ๋ก๊ทธ์ธ ์์ ์ ์งํํ๊ฒ ๋์๋๋ฐ, ์ค๋๋ง์ ํด๋ณด๋๊น ๊ธฐ์ต๋ ์ ์๋๊ณ ๊ทธ๋์ ์ด์ฐธ์ ์ธ์ธํ๊ฒ ์ ๋ฆฌ๋ฅผ ํด์ผ๊ฒ ๋ค๋ ๋ง์์ด ์๊ฒจ์ ๊ธ์ ์์ฑํ๊ฒ ๋ ๊ฒ์ด์๋ค.
์ธ์ธํ๊ฒ ์ ๋ฆฌ๊ฐ ํ์ํ๋ ๊ธฐ๋ฅ ์ค ํ๋๊ฐ ๋ฐ๋ก ํ ํฐ ๋ฌดํจํ(Revoke Token) ๊ธฐ๋ฅ์ธ๋ฐ, ์ด ๊ธฐ๋ฅ์ ์๋ฒ ํ๊ฒฝ์ ๊ฐ์ถ ์๋น์ค์์๋ ํด๋ผ์ด์ธํธ ์ ์ฅ์ผ๋ก๋ ํฌ๊ฒ ๊ณ ๋ฏผํ ํ์๊ฐ ์๋ ๊ธฐ๋ฅ์ด๊ธด ํ๋ค. ๋ง์ผ ์ ํ ๋ก๊ทธ์ธ์ ์ ๊ณตํ๋ ์๋น์ค์ธ๋ฐ ํ ํฐ ๋ฌดํจํ์ ๋ํด์ ์ ๋ชจ๋ฅด์ ๋ค๋ฉด ์๋ง๋ ์ด์ํ๋ ์๋น์ค์ ๋ฐฑ์๋์์ ์ฒ๋ฆฌํ๊ณ ์์ ๊ฐ๋ฅ์ฑ์ด ๋๋ค.
์ ํ์์๋ ์๋ฒ์์ ์ํํ๋๋ก ํ๋ ์์ฒญ์ธ๋ฐ, 1์ธ ๊ฐ๋ฐ ๋๋ Serverless ํ๊ฒฝ์์ ์ด์์ ํ๊ฒ ๋๋ ๊ฒฝ์ฐ์๋ ์ด์ฉ ์ ์์ด ๊ฒฐ๊ตญ ํด๋ผ์ด์ธํธ์์ ํด๊ฒฐํด ์ฃผ์ด์ผํ๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ์์ ํ ํฐ ๋ฌดํจํ(Revoke Token)๋ฅผ ์ด๋ป๊ฒ ์์ฒญํ๊ณ ์ฒ๋ฆฌํด์ผ ํ๋์ง ์์๋ณด๋๋ก ํ๊ฒ ๋ค.
ํ ํฐ ๋ฌดํจํ(Revoke Token)๋ ๋ฌด์์ผ๊น? ์ฌ์ค ๋ณ๊ฑฐ ์๋ค. ์๋น์ค์ ์ ํ ๊ณ์ ๊ฐ์ ์ฐ๊ฒฐ์ ํด์ ํด ์ฃผ๋ ๊ธฐ๋ฅ์ด๋ค.
์ ํ์์๋ง ์ํํด์ผ ํ๋ ๊ธฐ๋ฅ์ ์๋๊ณ ๋ชจ๋ ์์ ๋ก๊ทธ์ธ ์ ์ฒด๊ฐ ์๊ตฌํ๋ ๊ธฐ๋ฅ์ด๋ค.
ํ์ ํํด์๋ ๊ณ์ ์ ๋นํ์ฑํํ ๊ฒฝ์ฐ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ง์ฐ๊ฑฐ๋ ๋นํ์ฑ ์ฒ๋ฆฌ๋ฅผ ํ๊ฒ ๋๋๋ผ๋ ์์ ๋ก๊ทธ์ธ๊ณผ์ ์ฐ๊ฒฐ์ด ๋์ด์ง๋ ๊ฒ์ ์๋๋ค. ๊ฐ ์์ ๋ก๊ทธ์ธ์ ์ฐ๊ฒฐ๋ ๊ณ์ ์ ์ดํด๋ณด๋ฉด ๋ด๊ฐ ํํดํ ์๋น์ค๊ฐ ์์ง๋ ์ ๊ณต๋ ์๋น์ค๋ก ๋จ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์๋ฐ, ๋ด ๊ณ์ ๊ฐ์ ์ฐ๊ฒฐ์ ํด๋น ์๋น์ค์์ ํด์ ๋ฅผ ์์ฒญํ์ง ์์๊ธฐ ๋๋ฌธ์ ์ฌ์ ํ ์ฐ๊ฒฐ๋ ์ํ๋ก ๋จ์์๋ ๊ฒ์ด๋ค.
![]() |
![]() |
์์์ ์ค๋ช
ํ ๊ฒ๊ณผ ๊ฐ์ด ์ ํ ๋ก๊ทธ์ธ์ ์ต์ด ์ฐ๊ฒฐ์์๋ง email, fullName ์ ๋ณด๋ฅผ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ๋ง์ผ ํํดํ ์ฌ์ฉ์๊ฐ ์ฌ ๊ฐ์
์ ์งํํ๊ฒ ๋์์ ๋์๋ ์ฐ๊ฒฐ์ด ๋์ด์ ธ ์์ง ์๋ค๋ฉด, ์ด๋ฌํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
๊ฐ์ธ์ ๋ณด๊ฐ ์ค์ํ ๋งํผ, ์ ์์ ์ผ๋ก ์ฐ๊ฒฐ์ ํด์ ํ์ฌ ์์ ํ๊ฒ ์ฒ๋ฆฌํ๋๋ก ํ์.
ํ ํฐ ๋ฌดํจํ(Revoke Token)๋ฅผ ์ํํ๋ ์ ์ฐจ์ ๋ํด์ ๋จผ์ ์ค๋ช ํ๊ฒ ๋ค.
1. ์ ํ ๋ก๊ทธ์ธ์ผ๋ก authorizationCode ํ์ธ
2. JWT(Client Secret) ์์ฑ
3, AccessToken or RefreshToken ๋ฐ๊ธ
4. Revoke ์์ฒญ
์ ์ฐจ๋ฅผ ๋ณด๋ฉด ๋ง์ด ๋ณต์กํด ๋ณด์ด์ง๋ ์๋๋ฐ, ํด์ผํ ์์ ์ด ์๊ฐ๋ณด๋ค๋ ๋ง์ต๋๋ค.. ์ ๋ฐ๋ผํ์ ์ ๋ชจ๋ ์ ๊ตฌํํ์๊ธธ ๋ฐ๋๋๋ค.
JWT(Client Secret) ์์ฑ์ ์ํด์๋ ์ํธํ๋ ํ๋ผ์ด๋น ํค๋ฅผ ๋ฐ๊ธ๋ฐ์ ํ ํฐ ์์ฑ์ ์ธ์ฆ์ ์งํํด ์ฃผ์ด์ผ ํ๊ธฐ ๋๋ฌธ์, ์ฐ์ ํค๋ฅผ ๋ฐ๊ธ๋ฐ๋๋ก ํ์.
Developer์ Keys ํญ์์ ์์ฑ์ ์งํํด ์ฃผ๋ฉด ๋๋๋ฐ, ๋ง์ฝ์ Sign in with Apple ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ณ ์๋ ์๋น์ค๋ก ํค๋ฅผ ์ด๋ฏธ ๋ฐ๊ธ๋ฐ์ ์ํ๋ผ๋ฉด ๊ธฐ์กด ํค๋ฅผ ์ฌ์ฉํ์ ์ผ ํ๋ค.
Key ๋ฐ๊ธ ํ ๋จ ํ๋ฒ๋ง ๋ค์ด๋ก๋ ๊ฐ๋ฅํจ - ์ ๋ณด๊ดํด์ผ ํจ.
Key Name๊ณผ Description์ ์์ฑํด์ฃผ์. ์ด์์ค์ธ ์๋น์ค์ ํค๋ ์ญ์ ๋๋ฉด ๋ณต์กํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ ๋ฐ๋์ Key ๊ด๋ฆฌ๋ ์ ์คํ๊ฒ ํด์ฃผ์ด์ผ ํ๋ค. ๋ค์ด๋ฐ์ ์ผ๋ฐ์ ์ผ๋ก ์๋น์ค๋ช ์ ๋ฃ์ด์ค๋ค.
ํ๋จ์ Sign in with Apple ๊ธฐ๋ฅ์ ์ฒดํฌํ๊ณ Configure๋ฅผ ๋๋ฌ์ฃผ์.
๊ธฐ์กด ํค๋ฅผ ์ฌ์ฉํ์๋ ๊ฒฝ์ฐ์๋ ๊ธฐ์กด ํค๋ฅผ ์ ํํด ์ ์ฐจ๋ฅผ ์ํํ์๋ฉด ๋๋ค.
์๋น์คํ๋ ์ฑ์ ์ ํํด ์ ์ฅํ๊ณ ๋ฐ๊ธ ์ ๋ณด๊ฐ ๋ง์ผ๋ฉด Register๋ฅผ ๋๋ฌ ํค๋ฅผ ์์ฑํด์ฃผ์.
![]() |
![]() |
์ฌ๊ธฐ์ Download ๋ฒํผ์ด ํ์ฑํ ๋๋ฉด ๋ค์ด๋ก๋ ํ์.
๋ค์ด๋ก๋๋ ๋จ ํ๋ฒ๋ง ๊ฐ๋ฅํ๋, ๋ค์ด๋ฐ์ .p8 ํ์ผ์ ์์ด๋ฒ๋ฆฌ์ง ์๊ฒ ์ ๋ณด๊ดํด ์ฃผ์.
* ํ ์คํธ ๋๋ ์์ง ํค๋ฅผ ์ฌ์ฉํ์ง ์์๋ค๋ฉด ์ธ์ ๋ ์ง ์ญ์ ํ ์ฌ๋ฐ๊ธ ๋ฐ์ ์ ์์.
์์ ์ ๋ฆฌํ ํ๋ก์ฐ๋๋ก ์งํ์ ํด๋ณด๋ฉด ํ ํฐ ๋ฌดํจํ๋ฅผ ์ํด ํด๊ฒฐ ํด์ผํ ๋ถ๋ถ์ด authorizationCode๋ฅผ ๊ฐ์ ธ์ค๋ ๋ถ๋ถํ๊ณ , JWT๋ฅผ ์์ฑํ๋ ๋ถ๋ถ์ผ ๊ฒ์ด๋ค.
์๋ง๋ ๊ธฐ์ต๋์ง ์์ ์๋ ์๊ฒ ์ง๋ง, authorizationCode๋ฅผ ๊ฐ์ ธ์ค๋ ๋ถ๋ถ์ ์ฐ๋ฆฌ๊ฐ ์ด๋ฏธ ์งํํ ๋ด์ฉ์ด์๋ค. ๋ฐ๋ก ์ ํ ๋ก๊ทธ์ธ์ ํตํด์ ์ป์ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
OAuth 2.0 ์ธ์ฆ ํ๋ก์ธ์ค์์ Token์ ๋ฐ๊ธ ๋ฐ๊ธฐ ์ํ ์ผํ์ฑ ์ฝ๋๊ฐ authorizationCode ์ด๋ค. ํด๋น ์ฝ๋๋ ๋ง๋ฃ ์๊ฐ์ด ์งง๊ณ , ์ผํ์ฑ ์ฝ๋์ด๊ธฐ ๋๋ฌธ์ ๋ณ๋๋ก ๋ณด๊ดํ๋ค๊ณ ํด๋ ์๋ฏธ๊ฐ ์๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ก ์ธํ์ฌ ํ์ ํํด๋ฅผ ์ฒ๋ฆฌํ ๋, ์์ ๊ณ์ ๊ณผ์ ์ฐ๊ฒฐ์ ํด์ ํด์ฃผ๊ธฐ ์ํด์ ๋ค์ ํ ๋ฒ ๋ก๊ทธ์ธ์ ์์ฒญํ๊ฒ ๋๋๊ฒ์ด๋ค.
authorizationCode๋ ๋ก๊ทธ์ธ์ ์์ฒญํด ๊ฐ์ ธ์ฌ ์ ์์ผ๋, ๋ค์์ผ๋ก JWT ์์ฑ ๋ถ๋ถ์ ์ดํด๋ณด์.
Apple OAuth ํ๋ก์ธ์์์๋ ํด๋ผ์ด์ธํธ๊ฐ ์ธ์ฆ ์๋ฒ์ ์์ฒญํ ๋ Client Secret์ ์ฌ์ฉํด ์ธ์ฆ ์์ฒญ์ ์งํํ๊ฒ ๋๋๋ฐ, ์ด Client Secret์ ์ฌ์ค JWT๋ก ๊ตฌ์ฑ๋์ด ์๋ค๊ณ ํ๋ค.
JWT๋ฅผ ์์ฑํด Client Secret๋ฅผ ๋ง๋ค๊ธฐ ์ํด์๋ clientId, teamId, keyId, privateKey๊ฐ ํ์ํ๋ค.
- clientId : Bundle Identifier
XCode์์ ํ์ธ ๊ฐ๋ฅ
- teamId
Apple Developer์์ ํ์ธ ๊ฐ๋ฅ
- keyId
๋ฐ๊ธ๋ฐ์ ์ํธํ ํค์ ID
- privateKey
.p8 ํ์ผ์์ ํ์ธ ๊ฐ๋ฅ, ์ํธํ ํค ๋ฐ๊ธ์ ๋ค์ด๋ก๋ ๋ฐ์ ํ์ผ์
.p8์ด ์ด๋ฆฌ์ง ์๋ ๋ถ๋ค์ ํ ์คํธ ํธ์ง๊ธฐ๋ก ์ด์ด์ฃผ์๋ฉด ๋๋ค.
๐ฅ ์ฌ๊ธฐ์ ๋งค์ฐ ์ค์ํ ๋ถ๋ถ์ด ์๋๋ฐ, ์ํธํ ํค์ ๊ด๋ จ๋ ์ด๋ค ๋ฐ์ดํฐ๋ ๊ณต๊ฐ๋์๋ ์๋๋ ๋ฐ๋์ ๋ ธ์ถ๋์ง ์๋๋ก ์ฃผ์ํด ์ฃผ์ธ์ !!!!
์ ๋ ์์๋ก ๋ธ๋ก๊ทธ ์์ฑ์ ์ํด์ ๋ฐ๊ธ ๋ฐ์ ํค์ด๊ธฐ ๋๋ฌธ์, ํด๋น ๋ธ๋ก๊ทธ ๋ด์ฉ๊ณผ ๊ด๋ จ๋ ๋ชจ๋ ์ํธํ ํค๋ ์๋ณ์ ๋ค์ ์ ๋ถ ์ญ์ ์์ ์ ๋๋ค. ์ฌ๋ฌ๋ถ๋ค์ ์ ๋ ๋ ธ์ถ ์ํค์๋ฉด ์๋์ !!
Flutter์์ JWT์์ฑ์ ๊ฐ๋จํ๊ฒ ํด์ฃผ๋ ํจํค์ง์ API ํธ์ถ์ ์ํ ํจํค์ง๋ฅผ ์ถ๊ฐํด์ฃผ์.
dependencies:
dart_jsonwebtoken: ^2.17.0
http: ^1.3.0
์์ ์ดํด๋ณธ id๋ ํค๋ฅผ ์์ญ์ ๋ง๊ฒ ๋ณ๊ฒฝํด ์ฃผ์๋ฉด ๋๊ณ , iat, exp๋ JWT ์์ฑ์๊ฐ๊ณผ ๋ง๋ฃ์๊ฐ์ ๋ช ์ํด์ฃผ๋ ๊ฒ์ด๋ค. ๋ง๋ฃ์๊ฐ์ด ์ง๋๋ฉด ๋น์ฐํ ์ ํ ์๋ฒ๋ก ๋ถํฐ ์ธ์ฆ์ด ๊ฑฐ๋ถ๋๋ค.
๊ฐ์๊ฐ ๊ตฌํํ๋ ๋ก์ง์ ๋ง๊ฒ ๋ง๋ฃ์๊ฐ์ ์ค์ ํด ์ฃผ์๋ฉด ๋๋ค.
JWT({
"iss": {your_team_id},
"iat": DateTime.now().millisecondsSinceEpoch ~/ 1000,
"exp": (DateTime.now().millisecondsSinceEpoch ~/ 1000) + 3600,
"aud": "https://appleid.apple.com",
"sub": {your_client_id},
},
header: {
"alg": "ES256",
"kid": {your_key_id},
},
);
์ด์ ์์ฑํ JWT๋ก Clent Secret์ ๊ตฌ์ฑํด ์ฃผ๋๋ก ํ์.
๊ตฌ์ฑ์ ์์ .p8ํ์ผ์ ์ํธํ ํค๋ฅผ ์ฌ์ฉํด์ผ ํ๋๋ฐ, ํค๋ฅผ ์ฌ์ฉํ ๋์ ์ฃผ์ํ ๋ถ๋ถ์ ํ์ผ ๋ด์ ๋ชจ๋ ํ ์คํธ๊ฐ ํฌํจ๋์ด์ผ ํ๋ค๋ ๊ฒ๊ณผ ์ค๋ฐ๊ฟ ์ํ๋ ์ ์ง๋์ด์ผ ํ๋ค๋ ๊ฒ์ด๋ค.
const String privateKey =
"-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgL11OH66VJoxfxZSQ\nTGpTW4vcXPVylbGYigSACKFr212gCgYIKoZIzj0DAQehRANCAARZlXVBlPY2L8Ao\n4kF61H0zzcSpTmEqVU22SJKOnIf3wnS6zNxBfIfl5q7IyUXcr21c5ni5zuMY8OGT\nv3NQQVpd\n-----END PRIVATE KEY-----";
const String privateKey = '''-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgL11OH66VJoxfxZSQ
TGpTW4vcXPVylbGYigSACKFr212gCgYIKoZIzj0DAQehRANCAARZlXVBlPY2L8Ao
4kF61H0zzcSpTmEqVU22SJKOnIf3wnS6zNxBfIfl5q7IyUXcr21c5ni5zuMY8OGT
v3NQQVpd
-----END PRIVATE KEY-----''';
Client Secret์ ๋ง๋ค์ด ์ฃผ๊ธฐ ์ํด ์์ฑํ JWT์ Private Key๋ก ์๋ช (Signature)์ ์งํํด ์ฃผ๋ฉด ๋๋ค.
์ฌ๊ธฐ์ ES256์ Apple์ด ์๊ตฌํ๋ JWT ์๋ช ์๊ณ ๋ฆฌ์ฆ์ด๋ฉฐ, Elliptic Curve Digital Signature Algorithm(ECDSA)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ค.
const String privateKey = '''...''';
final JWT jwt = JWT({...});
final String clientSecret = jwt.sign(
ECPrivateKey(privateKey),
algorithm: JWTAlgorithm.ES256,
);
Client Secret์ ์์ฑ ํ์์ผ๋, ์ด์ API ๊ด๋ จ๋ ํต์ ๋ง ์ํํ๋ฉด์ ์ฐ๊ฒฐ์ ํด์ ํ ์ ์๊ฒ ๋์๋ค.
Apple์ Revoke Tokens API๋ฅผ ํธ์ถํ๊ธฐ ์ํด์๋ Access(or Refresh) Token์ด ํ์ํ๊ธฐ ๋๋ฌธ์, ํ ํฐ์ ๋จผ์ ๋ฐ๊ธ๋ฐ๋๋ก ํ์.
ํ ํฐ์ ์์ ์์ฑํ Client Secret๊ณผ ์ ํ ๋ก๊ทธ์ธ์ ํตํด ๊ฐ์ ธ์จ authorizationCode๋ก Access Token ๋๋ Refresh Token์ ๋ฐ๊ธ ๋ฐ์ ์ ์๋ค.
Future<void> getRefreshToken(String authorizationCode) async {
try {
final String clientSecret = _createAppleClientSecret();
final http.Response response = await http.post(
Uri.parse("https://appleid.apple.com/auth/token"),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: {
"client_id": {your_client_id},
"client_secret": clientSecret,
"code": authorizationCode,
"grant_type": "authorization_code",
"redirect_uri": "REDIRECT_URL",
},
);
if (response.statusCode == 200) {
// final data = json.decode(response.body);
// data["refresh_token"];
// data["access_token"]
}
} catch (_) {}
}
์ ์์ ์ผ๋ก Access Token๊ณผ Refresh Token์ ๋ฐ๊ธ ๋ฐ์๋ค.
๋๋์ด ๋ง์ง๋ง์ผ๋ก Apple์ ์๋น์ค์์ ๊ณ์ ์ฐ๊ฒฐ ํด์ ๋ง ์์ฒญํ๋ฉด ๋์ด๋ค !
Apple์ Revoke Tokens API ์์ฒญ์์๋ Client Secret์ ํ์ํ๋ค.
Revoke Token์ API๋ ์๋ต ๊ฐ์ด ์์ผ๋ฉฐ, 200 ์ฝ๋๋ก ์ฑ๊ณต ์ฌ๋ถ๋ฅผ ํ์ธํ๊ฑฐ๋ ์๋๋ฉด ์ฌ์ฉํ ํ ํฐ์ ๋ค์ ๊ฒ์ฆ ๋ฐ์ ์ฐ๊ฒฐ ํด์ ๋ฅผ ํ์ธํ๋ฉด ๋๋ค.
Future<void> revokeToken(String refreshToken) async {
try {
final String clientSecret = createClientSecret();
final http.Response response = await http.post(
Uri.parse("https://appleid.apple.com/auth/revoke"),
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: {
"client_id": {your_client_id},
"client_secret": clientSecret,
"token": refreshToken,
"token_type_hint": "refresh_token",
},
);
if (response.statusCode == 200) {
// success
}
} catch (_) {}
}
์ฐ๊ฒฐ์ด ์ ์์ ์ผ๋ก ํด์ ๋์๋ค๋ฉด, Apple์ ๊ณ์ ์ ๋ณด ๋ณ๊ฒฝ์ ๋ํ ์๋ด Email์ ์ฌ์ฉ์์๊ฒ ๋ฐ์กํ๋ค.
Mac ๋๋ iPhone์์ Apple ๊ณ์ ์ ์ ์ํด๋ณด๋ฉด, Apple๋ก ๋ก๊ทธ์ธํ ๋ด์ญ์ ํ์ธํ ์ ์๋ค.
Revoke Tokens ์์ฒญ์ ์ ์์ ์ผ๋ก ์๋ฃํ๊ฒ ๋๋ฉด ๋ด์ญ์์ ๋ ์ด์ ์ฐ๊ฒฐ๋ ์๋น์ค๊ฐ ๋ณด์ด์ง ์๊ฒ ๋๋ค.
![]() |
![]() |
ํ์ ํํด ๋๋ ๋นํ์ฑํ ๋ฑ์ Apple๊ณผ์ ๊ณ์ ์ฐ๊ฒฐ์ ํด์ ํ๊ธฐ ์ํ ์ ์ฐจ๋ฅผ ์ ๋ฆฌํด๋ณด๋ฉด, 1๏ธโฃauthorizationCode๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด ์ ํ ๋ก๊ทธ์ธ์ ์์ฒญํ๊ณ 2๏ธโฃ์ํธํ ํค๋ฅผ ์ฌ์ฉํ์ฌ JWT ์๋ช ์๊ณ ๋ฆฌ์ฆ์ผ๋ก Client Secret์ ์์ฑ, 3๏ธโฃAccess (or Refresh) Token์ ๋ฐ๊ธ ๋ฐ์ 4๏ธโฃRevoke Tokens API๋ฅผ ์์ฒญํ๋๋ก ํด์ฃผ๋ฉด ๋๋ค.
ํ ํฐ ๋ฌดํจํ์ ๋ํด์ ์์๋ณด์๋๋ฐ, ๋ง์ง๋ง์ผ๋ก ์์์ ์ฌ์ฉํ ํ๋ผ์ด๋น ํค๋ฅผ ์จ๊ธฐ๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์๊ฐํด ๋ณด๋ ค๊ณ ํ๋ค.
์ ์จ๊ฒจ์ผ ํ ๊น ? ๊ฐ๋ฐ์ ํ๋ฉด์ ํ๊ณผ ํ์
ํ๊ฑฐ๋ ์คํ์์ค ํ๋ก์ ํธ๋ฅผ ๊ด๋ฆฌํ ๋, ํ๋ผ์ด๋น ํค(API ํค, ์๋น์ค ๊ณ์ ํค, ์ธ์ฆ ์ ๋ณด ๋ฑ)๊ฐ ์ค์๋ก Git์ ์ปค๋ฐ๋๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
์ด๋ฌํ ํค๊ฐ ์ธ๋ถ์ ๋
ธ์ถ๋๋ฉด ๋ณด์ ์ฌ๊ณ ๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฉฐ, ์
์์ ์ธ ์ฌ์ฉ์ผ๋ก ์ธํด ์์คํ
์ด ์ํ์ ์ฒํ ์ ์๋ค. .gitignore, .env, ํ๊ฒฝ ๋ณ์(Environment Variables), Secret Manager ๋ฑ์ ์ ๋ง ๋ค์ํ ๋ฐฉ๋ฒ์ผ๋ก ํค๋ฅผ ์จ๊ธฐ๊ณ ๋ณด๊ดํ ์ ์์ผ๋ ๊ฐ ํ๊ฒฝ๊ณผ ํ์ ์ฑ๊ฒฉ์ ๋ง๋ ๋ฐฉ๋ฒ์ ์ฐพ์ ์ฌ์ฉํ์๋ฉด ๋๋ค.
์ด๋ฒ์ ์๊ฐํ ๋ฐฉ๋ฒ์ Configiguration File๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด๋ค.
๋น๊ณต๊ฐ ํค๋ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉํ๋ ์ค์ , ๋น๋ ๋ณํ์ ๋ฐ๋ฅธ ํ๊ฒฝ ์ค์ ๋ฑ์ Config ํ์ผ๋ก ๊ด๋ฆฌํ์ฌ ์ธ๋ถ ์ ์ฅ์์ ์ฌ๋ฆฌ์ง ์๊ณ ํ ๋ด์์๋ง ๊ณต์ ํ์ฌ ์ฌ์ฉํ๋๋ก ํ๋ ๋ฐฉ๋ฒ์ด๋ค.
์ฌ์ฉ ๋ฐฉ๋ฒ์ ๊ฐ๋จํ๋ค .gitignore์ Config ํ์ผ์ ๋ฑ๋กํ์ฌ ์ธ๋ถ ์ ์ฅ์์ ์ปค๋ฐ๋์ง ์๋๋ก ํ๊ณ , Config ํ์ผ์ ์ฑ ์ด๊ธฐ ์ค์ ๋๋ ํ์ํ ์์ ์ ๋ก๋ํ์ฌ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
ํ๋ก์ ํธ ๋ด assets ํด๋๋ฅผ ์์ฑํด์ฃผ๊ณ , config.json ํ์ผ์ ์ถ๊ฐํ์ฌ ์จ๊ธฐ๊ณ ์ถ์ ๋ฐ์ดํฐ๋ค์ ๋ณด๊ดํด ์ฃผ๋ฉด ๋๋ค.
![]() |
![]() |
config.json ํ์ผ์ ์จ๊ธฐ๊ณ ์ถ์ ์ค์ ์ด๋ ๋ฐ์ดํฐ๋ฅผ ๋ง๋ค์ด์ฃผ์.
{
"clientId": {your_client_id},
"teamId": {your_team_id},
"keyId": {your_key_id},
"privateKey": {your_private_key}
}
.gitignore ํ์ผ์ ์จ๊ธฐ๊ณ ์ถ์ ํด๋๋ฅผ ์ถ๊ฐํด ์ฃผ๋ฉด, Git์ ํฌํจ๋์ง ์๋๋ค. privates ํด๋ ์ ๋ถ๋ฅผ ์ ์ธ ์ฒ๋ฆฌํ์๋ค.
assets/privates/
Flutter์์ json ํ์ผ์ ๋ก๋ํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
final String configJson = await rootBundle.loadString("assets/privates/config.json");
final Map<String, dynamic> data = json.decode(configJson);
print(data);
Sign in with Apple ๊ธฐ๋ฅ์ ๋ํ ์ค์ ๋ถํฐ ๋ก๊ทธ์ธ ๋ฐ ํํด์ ํ ํฐ์ ๋ฌดํจํํ๋ ๋ฐฉ๋ฒ๊น์ง ์์๋ณด์๋ค.
์๊ฐ๋ณด๋ค ๊ธ์ ๋ด์ฉ์ด ๋๋ฌด ๊ธธ์ด์ ธ ์์ฝ๊ฒ๋ ์๋๋ก์ด๋์์ ์ ํ ๋ก๊ทธ์ธ์ ์ฌ์ฉํ๋ ๋ถ๋ถ์ ๋ํด์ ๋ค๋ฃจ์ง ๋ชปํ์๋๋ฐ, ์๋๋ก์ด๋๋ ์น ๊ด๋ จ๋ ๋ฐฉ๋ฒ์ ์ถํ ๊ธ์ ์์ฑํ ์์ ์ด๋ค.
๊ธ์ ํ ๋ฒ์ ์์ฑํ๋๊ฒ ์๋๋ผ ์๊ฐ๋ ๋๋ง๋ค ์กฐ๊ธ์ฉ ์์ฑํ๋ค ๋ณด๋ ์คํ๋ ํ๋ฆฐ ๋ถ๋ถ์ด ์์ ์๋ ์์ต๋๋ค.
๋ช ๋ฌ ๋ง์ ๊ธ์ ๊ฒ์ํ๊ฒ ๋์๋๋ฐ, ์์ผ๋ก๋ ๋ค์ ์ฃผ๊ธฐ์ ์ผ๋ก ๊ธ์ ์์ฑํ ์์ ์ ๋๋ค. Flutter์ ๊ด๋ จํ์ฌ ๊ถ๊ธํ ์ ์ด๋ ์๊ณ ์ถ์ ๊ธฐ๋ฅ์ด ์์ผ์๋ฉด ์ธ์ ๋ ์ง ์์ฒญํด ์ฃผ์ธ์.
๊ธด ๊ธ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
Sign in with Apple ๊ธฐ๋ฅ๊ณผ ๊ด๋ จ๋ ๋ถ๋ถ์ด๋ ๊ธฐํ ๊ถ๊ธํ์ ๋ด์ฉ์ ๋๊ธ ๋จ๊ฒจ์ฃผ์๋ฉด ์ต๋ํ ์ ์ํ๊ฒ ๋ต๋ณ ๋จ๊ธฐ๋๋ก ํ๊ฒ ์ต๋๋ค ๐ฏ