Flutter 기반 iOS 앱에 카카오 로그인 설정하기

업데이트

2026년 8월 8일

개요

경고

이 문서는 개발을 끝낸 후 몇 달이 지나 기억과 ai를 이용해서 작성했다. 최근 개발하는 시점에서는 이 문서와 일치하지 않을 수 있다.

Flutter를 이용해 앱을 개발할 때 카카오 로그인 기능을 구현해야 할 때가 있다. 이 경우 kakao_flutter_sdk 같은 카카오 네이티브 SDK를 쓰지 않는다. 대신 REST API 키로 카카오 authorize 페이지를 직접 여는 웹 OAuth 방식을 사용한다. 흔한 “카카오 SDK 연동” 가이드와 달리 iOS 네이티브 앱 키 등록이나 번들 ID 등록이 필요 없다 — Redirect URI 등록과 REST API 키만 있으면 된다.

노트

전체 OAuth 설계(2단계 인증 흐름, provider별 응답 형식 차이 등)는 OAuth2 소셜 로그인 문서에 정리되어 있다. 이 문서는 그중 Flutter + iOS(시뮬레이터/실제 기기) + 로컬 백엔드 조합에서 실제로 돌려보는 방법에 집중한다.

이 문서가 다루는 범위는 다음과 같다.

  1. Kakao Developers에서 로컬 백엔드용 Redirect URI 등록
  2. iOS 프로젝트의 URL Scheme(딥링크) 확인
  3. .env 환경변수 설정
  4. 로컬 Django 서버 + iOS 시뮬레이터 동시 실행
  5. 실제 기기(iPhone)에서 테스트할 때 추가로 필요한 설정 (개발서버 외부 접근 허용)
  6. 로그인 전체 시퀀스 이해 및 트러블슈팅
힌트

시뮬레이터 vs 실제 기기: 시뮬레이터는 Mac과 네트워크 네임스페이스를 공유하므로 http://localhost:8000으로 바로 접근된다. 실제 기기는 별도의 네트워크 장치라 localhost가 iPhone 자신을 가리켜 접속이 안 된다 — Mac IP로 개발서버를 열어야 하며, 필요한 추가 설정은 6절에 정리했다.

1. Kakao Developers — Redirect URI 등록

Kakao Developers애플리케이션 추가.

> 일반 > 앱 기본 정보:

  • 앱 아이콘 등록
  • 카카오 회원 인증 또는 사업자 번호 입력 → 비즈앱 등록 (이메일 scope 필요 시)

카카오 로그인 > 일반:

  • 사용 설정: On
  • OpenID Connect: On

카카오 로그인 > 개인정보:

  • 닉네임
  • 프로필 사진
  • 카카오 계정(이메일)

카카오 로그인 > Redirect URI:

http://localhost:8000/api/auth/kakao/callback/
중요

앱이 직접 리다이렉트를 받지 않는다. iOS 앱은 https:// 퍼블릭 URL을 가질 수 없으므로, Kakao의 Redirect URI는 항상 로컬 백엔드(localhost:8000)를 가리킨다. 백엔드가 이 요청을 받아서 다시 앱의 커스텀 URL 스킴으로 302 리다이렉트하는 구조다 — 자세한 흐름은 7절 참고.

> 앱 키 > REST API 키 복사 → 뒤에서 KAKAO_CLIENT_ID로 사용.

노트

네이티브 앱 키 / iOS 번들 ID 등록은 건너뛴다. 카카오 로그인 > 플랫폼 > iOS 플랫폼 등록 화면에서 번들 ID를 요구하는 경우가 많은데, 이는 kakao_flutter_sdk(네이티브 SDK) 사용 시에만 필요하다. 이 프로젝트는 REST API 키로 kauth.kakao.com/oauth/authorize를 웹뷰로 여는 방식이라 Redirect URI 등록 하나로 충분하다.


2. iOS 프로젝트 — URL Scheme 확인

flutter_web_auth_2는 OAuth 콜백을 가로채기 위해 iOS CFBundleURLTypes에 등록된 커스텀 스킴이 필요하다. ios/Runner/Info.plist에 이미 등록되어 있다.

ios/Runner/Info.plist
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLName</key>
    <string>com.myapp.app</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>

이 스킴 문자열은 코드에서 다음 상수와 반드시 일치해야 한다.

lib/config/api_config.dart
class ApiConfig {
  // ...
  static const String deepLinkScheme = 'myapp';
}

새 프로젝트에 이 구조를 옮겨 쓸 경우, 이 두 곳(Info.plist + 상수)의 스킴 문자열을 같이 바꿔야 한다. 하나만 바꾸면 콜백이 영원히 캡처되지 않고 ASWebAuthenticationSession이 멈춘 것처럼 보인다.


3. .env 설정 (시뮬레이터용)

cp .env.sample .env

.env에서 카카오 관련 값만 채운다.

.env
KAKAO_CLIENT_ID=<REST API>

# API_BASE_URL은 주석 처리된 상태로 둔다 — 코드 기본값이 이미 localhost:8000
#API_BASE_URL=http://localhost:8000
lib/config/api_config.dart
static const String _defaultDebugBaseUrl = 'http://localhost:8000';
static const bool _isRelease = bool.fromEnvironment('dart.vm.product');

static String get baseUrl => _envBaseUrl.isNotEmpty
    ? _envBaseUrl
    : (_isRelease ? _defaultReleaseBaseUrl : _defaultDebugBaseUrl);

dart.vm.product가 false인 debug 빌드(시뮬레이터 실행은 항상 debug)에서는 API_BASE_URL을 비워두면 자동으로 http://localhost:8000을 쓴다.


4. 로컬 Django 서버 실행 (시뮬레이터)

백엔드 저장소에서:

python manage.py runserver

시뮬레이터는 Mac과 네트워크를 공유하므로 0.0.0.0 바인딩이나 ALLOWED_HOSTS 추가 없이 기본 127.0.0.1:8000이면 충분하다.

노트

실제 기기(iPhone)를 USB로 연결해서 테스트할 경우 이 명령으로는 안 된다 — 6절의 추가 설정이 필요하다.


5. iOS 시뮬레이터 실행

# 시뮬레이터 앱 열기 (선택)
make sim

# 기본 기기(iPhone 16e)로 실행
make iphone16

# 다른 기기로 실행
make iphone16 IPHONE16="iPhone 16 Pro"

make iphone16이 내부적으로 하는 일:

Makefile
define set_env
    @BUNDLE_ID=$$(grep '^BUNDLE_ID=' $(1) | cut -d'=' -f2); \
    TEAM=$$(grep '^DEVELOPMENT_TEAM=' $(1) | cut -d'=' -f2); \
    echo "APP_BUNDLE_ID=$$BUNDLE_ID" > ios/Flutter/Env.xcconfig; \
    echo "DEVELOPMENT_TEAM=$$TEAM" >> ios/Flutter/Env.xcconfig
endef

iphone16:
    $(call set_env,.env)
    flutter run -d "$(IPHONE16)" --dart-define-from-file=.env
  1. .envBUNDLE_ID / DEVELOPMENT_TEAM을 Xcode Env.xcconfig에 주입 (코드 서명용)
  2. .env 전체를 --dart-define-from-file로 Dart 컴파일 타임 상수에 주입 → ApiConfig.kakaoClientId 등이 채워짐

6. 실제 기기(iPhone)에서 테스트하기

실제 기기는 시뮬레이터와 달리 Mac과 별개의 네트워크 장치다. localhost:8000은 iPhone 자신을 가리키므로, 개발서버를 Mac의 로컬 IP로 외부에 열어야 한다.

6.1 Mac IP 확인

ipconfig getifaddr en0
경고

와이파이를 바꾸거나 재접속하면 IP가 달라질 수 있다(DHCP). 아래 2~4단계는 IP가 바뀔 때마다 다시 맞춰야 한다. 매번 다시 맞추는 게 번거로우면 Mac IP를 고정해두는 방법도 있다.

6.2 Django 서버를 외부에 개방

python manage.py runserver 0.0.0.0:8000

0.0.0.0으로 바인딩해야 Mac의 외부 인터페이스(Wi-Fi)로 들어오는 요청을 받는다. 기본값인 127.0.0.1은 Mac 자기 자신만 접근 가능하다.

ALLOWED_HOSTS에 Mac IP를 추가해야 Django가 요청을 거부하지 않는다.

settings.py
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'YOUR_MAC_IP']

또는 개발서버에서만 모두 허용하기 위해 다음과 같이 해도 된다.

settings/local.py
ALLOWED_HOSTS = ['*']
중요

처음 0.0.0.0으로 띄우면 macOS가 “수신 네트워크 연결을 허용하시겠습니까?” 방화벽 팝업을 띄울 수 있다. 허용을 눌러야 실제 기기에서 접속된다. 시스템 설정 → 네트워크 → 방화벽에서 나중에 다시 확인/허용할 수도 있다.

6.3 Kakao Developers에 Mac IP용 Redirect URI 추가

기존 localhost 항목은 시뮬레이터용으로 남겨두고, Mac IP 항목을 추가로 등록한다 (카카오 로그인 > Redirect URI).

http://localhost:8000/api/auth/kakao/callback/
http://<MAC_IP>:8000/api/auth/kakao/callback/

6.4 .env.device 설정

cp .env.sample .env.device
.env.device
KAKAO_CLIENT_ID=<REST API>          # 시뮬레이터용 .env와 동일한 값
API_BASE_URL=http://<MAC_IP>:8000      # 실제 기기 전용 — 반드시 채워야 함
중요

.env(시뮬레이터용)와 달리 API_BASE_URL비워두면 안 된다. 비어 있으면 ApiConfig.baseUrl이 debug 기본값인 http://localhost:8000으로 떨어지는데, 이는 iPhone 입장에서 자기 자신이라 절대 개발서버에 닿지 못한다 (A-8 참고).

6.5 실제 기기 연결 후 실행

Mac에 iPhone을 USB로 연결한 뒤:

make iphone
Makefile
iphone:
    $(call set_env,.env.device)
    flutter run -d $(shell flutter devices 2>/dev/null | grep '• ios' | grep -v simulator | head -1 | sed 's/.*• \([^ ]*\) *• ios.*/\1/') --dart-define-from-file=.env.device

.env 대신 .env.device를 읽어 Env.xcconfig와 Dart 컴파일 타임 상수를 채운다 — API_BASE_URL이 Mac IP로 주입되는 지점이 여기다.

최초 설치 시: 설정 → 일반 → VPN 및 기기 관리 → 개발자 앱에서 신뢰 필요. 무료 Apple 개발자 계정으로 서명한 경우 7일마다 재설치해야 한다.


7. 로그인 시퀀스

핵심 포인트: Kakao의 Redirect URI는 로컬 백엔드를 가리키고, 백엔드가 받은 code를 즉시 소비하지 않고 앱의 myapp:// 스킴으로 한 번 더 리다이렉트한다. flutter_web_auth_2는 이 두 번째 리다이렉트(커스텀 스킴)만 가로챈다. 실제 카카오 토큰 교환은 앱이 code를 다시 POST로 보낸 시점에 백엔드에서 일어난다.

노트

실제 기기 테스트 시: 위 다이어그램의 localhost:8000<MAC_IP>:8000으로 바꿔서 읽으면 된다 — 로직은 동일하고, redirect_uri/API_BASE_URL이 가리키는 호스트만 다르다.


부록 A: 트러블슈팅

공식 문서: Kakao Developers — 카카오 로그인 트러블슈팅

A-1. KOE006 (Kakao authorize 페이지)

원인: 등록되지 않은 redirect_uri로 인가 코드를 요청. 경로에 쿼리 파라미터를 붙이는 것도 실패 원인이 된다.

해결: Kakao Developers → Redirect URI가 정확히 http://localhost:8000/api/auth/kakao/callback/ (trailing slash 포함)와 1:1로 일치하는지 확인.

A-2. KOE101 (Kakao authorize 페이지)

원인: 앱 키 타입 오용 — REST API 호출/웹 authorize에는 REST API 키가 필요한데 JavaScript 키나 네이티브 앱 키를 넣은 경우, 혹은 오타.

해결: .envKAKAO_CLIENT_ID앱 > 앱 키 > REST API 키와 일치하는지 확인.

A-3. 로그인 버튼을 눌러도 카카오 페이지가 안 뜨거나 authorize 단계에서 즉시 에러

원인: .envKAKAO_CLIENT_ID가 비어 있는 상태로 빌드됨 — --dart-define-from-file이 적용 안 됐거나 .env 파일 자체가 없음.

해결: make iphone16으로 실행했는지 확인 (flutter run 을 인자 없이 직접 실행하면 --dart-define-from-file이 빠진다).

A-4. 로그인 페이지는 뜨는데 로그인 후 앱으로 안 돌아옴 (세션 멈춤)

원인: CFBundleURLSchemes(Info.plist)와 ApiConfig.deepLinkScheme 문자열 불일치, 또는 Info.plist 수정 후 clean build 안 함.

해결:

grep -A3 CFBundleURLSchemes ios/Runner/Info.plist
grep deepLinkScheme lib/config/api_config.dart
flutter clean && make iphone16

A-5. Connection refused / 네트워크 에러 (코드 POST 단계)

원인: 로컬 Django 서버가 안 떠 있거나 다른 포트에서 실행 중.

해결: python manage.py runserver8000 포트로 떠 있는지, .envAPI_BASE_URL이 비어있어 기본값(localhost:8000)을 쓰고 있는지 확인.

A-6. 매번 다른 카카오 계정으로 테스트하고 싶은데 자동 로그인됨

원인: 시뮬레이터 내 웹뷰(Safari 세션)에 카카오 로그인이 유지됨.

해결: 시뮬레이터에서 카카오 계정 로그아웃(브라우저로 accounts.kakao.com 접속 후 로그아웃) 하거나, 새 시뮬레이터 기기를 만들어 테스트.

A-7. Hot Restart 직후 콜백이 중복 처리되며 에러

원인: Authorization Code는 1회용. Flutter Hot Restart(R)로 상태가 재구성되며 이전 콜백이 다시 처리되는 경우가 있다 (Apple 로그인 문서의 “dev 모드 이중 실행”과 같은 부류의 문제).

해결: 로그인 플로우 테스트 중에는 Hot Restart 대신 앱을 완전히 재시작(qmake iphone16)해서 확인.

A-8. (실제 기기) 카카오 로그인 후 앱으로 안 돌아오거나 흰 화면 / 응답 없음

원인: .env.deviceAPI_BASE_URL을 비워둠 → ApiConfig.baseUrl이 debug 기본값인 http://localhost:8000으로 빠짐. iPhone 입장에서 localhost는 iPhone 자기 자신이라 Mac의 Django 서버에 닿을 수 없다.

해결: .env.deviceAPI_BASE_URL=http://<MAC_IP>:8000을 반드시 채운다 (6.4절 참고). 시뮬레이터용 .env처럼 주석 처리하면 안 된다.

A-9. (실제 기기) KOE006 — Mac IP로는 authorize까지 갔는데 콜백에서 실패

원인: Kakao Developers에 http://localhost:8000/...만 등록되어 있고 Mac IP 기반 Redirect URI(http://<MAC_IP>:8000/api/auth/kakao/callback/)는 등록 안 됨. 또는 Wi-Fi가 바뀌어 Mac IP 자체가 달라짐.

해결: ipconfig getifaddr en0로 현재 IP 재확인 → Kakao Developers Redirect URI 목록에 최신 IP로 갱신/추가.

A-10. (실제 기기) Django DisallowedHost 에러

원인: ALLOWED_HOSTS에 Mac IP가 없는 상태로 0.0.0.0:8000을 띄움.

해결: settings.pyALLOWED_HOSTS에 Mac IP 추가 후 서버 재시작.

A-11. (실제 기기) 서버는 떠 있는데 iPhone에서 아예 연결이 안 됨 (타임아웃)

원인: macOS 방화벽이 Python(Django)의 수신 연결을 차단. 또는 Mac과 iPhone이 서로 다른 Wi-Fi(예: iPhone이 개인 핫스팟/게스트망)에 붙어 있음.

해결: - 시스템 설정 → 네트워크 → 방화벽에서 Python/터미널 수신 연결 허용 - Mac과 iPhone이 같은 Wi-Fi에 연결되어 있는지 확인


참고 링크

맨 위로