DB 설계를 마치고 본격적인 기능 개발에 들어갔습니다.
첫 번째로 구현할 기능은 소셜 로그인으로, Google과 Kakao 로그인을 지원해야 했죠.
로그인은 서비스 진입의 첫 관문이기도 하고, 비즈니스 미팅에서도 실제 로그인 -> 홈 화면 진입을 보여줄 수 있어야 했습니다.
"웹에서 몇 번 해봤으니까 금방 되겠지"
하지만 앱에서는 그 생각이 완전히 틀렸다는 걸 곧 깨닫게 됩니다.
1. 웹에서는 쉬웠는데
웹에서 OAuth 소셜 로그인을 구현할 때는 비교적 단순했습니다.
// 웹에서의 Google OAuth
1. Google Cloud Console에서 클라이언트 ID 발급
2. 승인된 리디렉션 URI 등록 (https://myapp.com/auth/callback)
3. 프론트엔드에서 "구글 로그인" 버튼 클릭 시 OAuth 인증 페이지로 이동
4. 사용자가 구글 계정으로 로그인
5. 인증이 완료되면, 설정해둔 리디렉션 URL로 인증 코드 전달
6. 전달받은 코드로 백엔드에서 Access Token 요청
7. Access Token으로 사용자 정보 조회
8. 우리 서비스의 로그인 처리 및 토큰 발급
웹에서는 이 흐름이 브라우저 환경 안에서 자연스럽게 이어집니다.
그래서 개발자는 인증이 끝나면 어디로 돌아올지, 리디렉션 URL만 제대로 설정하면 대부분 작동했죠.
하지만 앱에서는 이 흐름이 그대로 적용되지 않았습니다.
2. Google OAuth - SDK와 SHA-1
Google 로그인부터 시작했습니다. 처음에는 웹처럼 REST API로 구현하려고 했습니다.
window.location.href = `https://accounts.google.com/o/oauth2/auth?client_id=...`;
하지만 앱에서는 이 방식이 권장되지 않아, 대신 Google SDK를 사용했습니다.
// SDK 방식
import { GoogleSignin } from '@react-native-google-signin/google-signin';
GoogleSignin.configure({
webClientId: 'YOUR_CLIENT_ID',
});
const userInfo = await GoogleSignin.signIn();
[왜 SDK를 써야 할까?]
앱에서 window.location.href 같은 방식으로 OAuth URL로 이동시키면, 실제로는 외부 브라우저(Chrome, Safari)가 열립니다.
문제는 OAuth 인증이 끝나면 설정된 리디렉션 URL로 이동하긴 하지만, 앱은 웹 브라우저처럼 그 URL을 처리할 수 없기 때문에 인증 흐름이 거기서 끊기게 됩니다.
그래서 앱의 AndroidManifest에 Deep Link URL 스킴과 패키지명을 등록해서 돌아올 경로를 알려주는 방법이 있습니다.
하지만 이 경우 iOS, Android 각각의 Deep Link 설정, OS 버전에 따른 예외 처리 등 추가 작업이 요구되고, 사용자 경험이 저하됩니다.
SDK는 이 문제를 해결해줍니다.
SDK 방식은 OS에서 제공하는 인증 화면을 사용하고, 앱 내부에서 로그인 과정을 처리한 뒤 결과를 바로 앱으로 반환해줍니다.
개발자는 리디렉션 URL을 직접 다룰 필요 없이, '로그인 버튼 클릭' -> '로그인 정보 반환'의 결과만 받으면 됩니다.
[SHA-1 지문]
그렇게 Google SDK를 사용하려 하니, 이번에는 SHA-1 지문을 등록해야 했습니다.
SHA-1 지문은 앱의 디지털 서명을 식별하는 값입니다.
웹은 URL을 기반으로 신뢰합니다. "이 URL에서 온 요청이면 믿을 수 있다"는 식이죠.
하지만 앱은 다릅니다. 앱은 누구나 만들 수 있고, 패키지명도 같게 만들 수 있습니다. 그래서 앱 자체의 서명을 확인합니다.
Gradle 명령어로 SHA-1 지문을 확인할 수 있었습니다. 그런데 지문이 2개나 나왔습니다.
cd android
./gradlew signingReport
Variant: debug
SHA1: A1:B2:C3:D4:E5:F6:G7:H8:I9:J0:K1:L2:M3:N4:O5:P6:Q7:R8:S9:T0
Variant: release
SHA1: (아직 생성 안 됨)
알고보니 Android 앱은 Debug와 Release 두가지 키로 서명됩니다.
Debug Key
- 개발 중에 사용
- Android Studio가 직접 생성
- 테스트용
Release Key
- 실제 배포할 때 사용
- 개발자가 직접 생성하고 보관
- 프로덕션용
Google OAuth가 작동하려면 두 지문을 모두 Google Cloud Console에 등록해야 합니다.
개발 환경에서는 Debug Key로 서명된 앱이 실행되고, 실제 사용자는 Release Key로 서명된 앱을 사용하니까요.
Debug SHA-1 지문을 Google Cloud Console의 Android 앱 설정에 추가하자, 앱에서 소셜 로그인이 잘 작동했습니다.
3. Kakao OAuth - 키 해시와 네이티브 설정
다음으로 Kakao 로그인을 구현을 위해 @react-native-seoul/kakao-login 라이브러리를 사용했습니다.
Kakao 개발자 문서를 보니, 키 해시라는 걸 등록해야 한다고 나와있었습니다.
키 해시도 SHA-1 지문과 비슷한 개념이었습니다. 앱의 서명을 확인하는 값이죠. 다만 Kakao는 Google과 형식이 달랐습니다.
방법 1) openssl로 키 해시 구하기
문서에 나온 대로 openssl 명령어로 키 해시를 구했습니다.
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64
나온 값을 Kakao 개발자 센터에 등록했지만, 에러가 발생했습니다.
에러: INVALID_REQUEST (400)
Curtom URL scheme is not enabled for your Android client
방법 2) Kakao SDK로 키 해시 구하기
비슷한 사례를 찾아보니 openssl로 구한 값과 실제 앱이 사용하는 값이 다를 수 있다고 했습니다.
가장 확실한 방법은 앱 코드에서 직접 키 해시를 출력하는 것이었습니다.
MainActivity.kt에 코드를 추가했습니다:
import android.content.pm.PackageManager
import android.util.Base64
import android.util.Log
import java.security.MessageDigest
class MainActivity : ReactActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getAppKeyHash()
}
private fun getAppKeyHash() {
try {
val info = packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNATURES
)
for (signature in info.signatures ?: emptyArray()) {
val md = MessageDigest.getInstance("SHA")
md.update(signature.toByteArray())
val keyHash = String(Base64.encode(md.digest(), Base64.NO_WRAP))
Log.d("KeyHash", "키 해시: $keyHash")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
앱을 다시 빌드하고 실행한 뒤, Android Studio의 Logcat에서 키 해시 값을 확인했습니다.
openssl로 구한 값과 실제 값이 달랐던 겁니다.
KeyHash: abc123XYZ789...
[네이티브 설정: AndroidManifest]
제대로 된 키 해시를 등록했지만, 앱을 초기화하고 다시 빌드하니 여전히 로그인이 되지 않았습니다.
문제는 환경 변수였습니다.
AndroidManifest.xml에 카카오 네이티브 앱 키를 설정해야 하는데
-- AndroidManifest.xml
<activity
android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="oauth" android:scheme="process.env.KAKAO_NATIVE_APP_KEY"/>
</intent-filter>
</activity>
strings.xml에는 이렇게 되어 있었습니다.
<string name="kakao_app_key">process.env.KAKAO_NATIVE_APP_KEY</string>
문제는 strings.xml은 JavaScript가 아니라는 점입니다.
process.env는 JavaScript 문법이라 Android 빌드 시스템에서는 그냥 문자열로 인식됩니다. 환경 변수를 읽어오지 않는 거죠.
환경 변수 대신 실제 키 값을 직접 입력했습니다.
-- AndroidManifest.xml
<meta-data
android:name="com.kakao.sdk.AppKey"
android:value="실제_네이티브_앱_키"/> //여기는 앞에 kakao 안 붙이기
<activity
android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="oauth" android:scheme="kakao실제_네이티브_앱_키"/>
</intent-filter>
</activity>
-- strings.xml
<string name="kakao_app_key">실제_네이티브_앱_키</string>
그제서야 Kakao 로그인이 제대로 작동했습니다.
4. 배운 점
가장 크게 느낀 건 웹과 앱이 신뢰를 확인하는 방식이 근본적으로 다르다는 것입니다.
웹은 URL 기반 신뢰로 서버를 검증하지만, 앱은 서명 기반 신뢰로 앱 자체를 검증합니다.
그래서 웹에서는 리디렉션 URL만 등록하면 되지만, 앱에서는 SHA-1 지문, 키 해시 같은 앱의 서명 정보를 등록해야 합니다.
SHA-1 지문, 키 해시 같은 개념은 공식 문서에 나와있습니다. 하지만 실전에서는 더 복잡했습니다.
공식 문서에 나와있던 openssl 값이 실제 값과는 달랐고, Debug과 Release도 차이가 있으며, 환경 변수 처리도 추가적으로 필요합니다.
이런 부분들은 직접 부딪혀봐야 알 수 있었습니다.
[네이티브 지식의 중요성]
React Native나 Expo 같은 프레임워크를 쓰면, 네이티브 코드를 몰라도 앱을 만들 수 있습니다.
하지만 결국 네이티브 기능을 활용하려면
- Android/iOS 빌드 시스템 이해
- Gradle 같은 도구
- AndroidManifest.xml 설정
이런 것들을 어느 정도 알아야 했습니다.
네이티브 지식의 부재로 인한 문제가 계속 발생했고, 사수가 없다보니 이런 문제들을 스스로 해결해야 했습니다.
그 과정에서 가장 도움이 됐던 건 공식 문서를 꼼꼼히 읽고, 직접 로그를 찍어보는 것이었습니다.
웹과 앱은 단순히 플랫폼만 다른 게 아니었습니다. 보안 모델, 빌드 시스템, 배포 방식이 모두 달랐습니다.
SHA-1 지문과 키 해시. 처음엔 생소했지만, 이제는 왜 필요한지 압니다.
앱은 웹보다 사용자와 더 가까이 있습니다. 기기에 설치되고, 개인정보에 접근하고, 카메라나 위치를 사용합니다.
그래서 더 엄격한 신뢰 확인이 필요한 거겠죠.
단순히 코드를 작성하는 것을 넘어서서, 앱이라는 플랫폼을 이해하는 것의 중요성을 깨달았습니다.
'개발 > 주니어 개발자의 캐시백 앱 단독 개발기' 카테고리의 다른 글
| 5. 인프라 입문: Android HTTP 차단과 HTTPS 적용기 (1) | 2026.01.21 |
|---|---|
| 4. 갑자기 해외 서비스도 추가된다고? (0) | 2026.01.19 |
| 2. 브랜드마다 다른 캐시백 정책, 어떻게 DB에 담을까? (0) | 2026.01.05 |
| 1. 웹에서는 당연했던 SVG가 앱에서는 당연하지 않았다 (0) | 2025.12.24 |
| 0. 기록을 남기기로 한 이유 (5) | 2025.12.22 |