[TIL] Flutter 9기 Day 3 Git이랑 좀 더 친해지기~ & Firebase 구글 로그인

현서·2025년 11월 26일

[TIL] Flutter 9기

목록 보기
15/102
post-thumbnail


-> 튜터님과 짧은 git 특강^^*

📝 브랜치 동기화

브랜치 동기화란?

보통 main을 기준으로 새로운 기능을 만들려고 feature 브랜치를 만든다.
그런데 시간이 지나면
A가 main에 새로운 기능을 merge함
B도 merge함
C도 merge함
그러면 내가 만들고 있는 feature 브랜치는 시간이 지날수록 main과 코드가 달라진다.

이때 생기는 문제

  • 나중에 PR 보낼 때 충돌 잔뜩 발생
  • 내 코드가 오래된 상태여서 최신 구조와 다름
  • 팀원들과 코드 구조가 맞지 않음

이럴 때 하는 것이 브랜치를 최신 main과 동기화하는 작업(브랜치 최신화)
이걸 하는 방법이 크게 두 가지
리베이스(Rebase)백머지(Back Merge)다.

리베이스와 백머지는 모두 “내 작업 브랜치에 최신 main을 가져오는 방법”이다.

1. 리베이스(Rebase)

내 브랜치를 최신 main 뒤에 다시 올려놓는 것

main:  A — B — C — D
                  ↑ 최신 merge됨

feature:   X — Y   (내 작업)

여기서 리베이스를 하면

main:  A — B — C — D
                        ↑ 최신 main 뒤에
feature:                   X' — Y'

X와 Y가 마치 D 뒤에 처음부터 만들어진 것처럼 재배치

특징

  • 히스토리가 일자 형태로 깔끔
  • 로그를 보면 매우 보기 좋음
  • 하지만 커밋 히스토리를 다시 만드는 작업이라 주의가 필요
  • 깔끔한 git 히스토리를 유지하고 싶을 때 많이 사용

최신화하는 방법

  1. main 업데이트 받기
git checkout main
git pull origin main
  1. 다시 작업 브랜치로 이동
git checkout feature/login
  1. 최신 main 위에 커밋 재배치
git rebase main

충돌이 나면?

터미널에 충돌 파일들이 표시됨

CONFLICT (content): Merge conflict in lib/main.dart
  1. 충돌난 파일을 열기
<<<<<<< HEAD
// main 브랜치 코드
=======
// feature 브랜치 코드
>>>>>>> feature/login
  1. 원하는 최종 형태로 수정
  2. 해결한 파일 stage 등록
git add 파일명
  1. 리베이스 계속
git rebase --continue

충돌 없을 때까지 반복.

  1. 리모트에 push
    리베이스는 히스토리가 바뀌므로 강제 push 필요.
git push --force-with-lease

git push --force-with-lease

리베이스 후에 원격 브랜치를 덮어쓰되,
다른 사람의 커밋은 절대 덮어쓰지 않도록 안전장치를 걸어놓은 push 방식

  • 왜 필요한가?
    리베이스를 하면 내 로컬 커밋의 해시가 완전히 바뀜
    그래서 원격 브랜치와 히스토리가 달라져서 그냥 push가 안 됨.

이때 강제로 push 해야 하는데:
git push --force
강제로 덮어씀
다른 팀원이 올린 커밋도 삭제될 위험 있음 → 매우 위험
git push --force-with-lease
강제로 push는 하되
원격 브랜치가 내가 마지막으로 pull 한 상태 그대로일 때만 덮어쓰기 허용
즉, 다른 팀원이 그 사이에 커밋 올리면 push를 막아줌 → 안전함

2. 백머지(Back Merge)

main을 내 브랜치로 끌어오는 merge

main:    A — B — C — D

feature:     X — Y

여기서 merge 하면

feature:  X — Y — M
                   ↑ merge commit
main의 변경을 feature로 가져온 상태

M이라는 merge commit이 생긴다.

특징

  • 히스토리에 merge commit이 남아 약간 복잡해짐
  • 하지만 안전하고 충돌 처리 명확
  • 실수할 위험이 적다
  • 충돌이 많거나 복잡할 때 주로 사용.

최신화하는 방법

  1. main 최신 상태 가져오기
git checkout main
git pull origin main
  1. 작업 브랜치로 이동
git checkout feature/login
  1. main을 내 브랜치로 merge
git merge main
  1. 충돌이 나면?
    터미널에 비슷하게 충돌 파일 나타남.
Auto-merging lib/home.dart
CONFLICT (content): Merge conflict in lib/home.dart
  • 충돌 해결 체크리스트
    - 충돌 파일 열기
    - 충돌 구간 수정
    - commit 하기
git add 충돌난파일
git commit
  1. push
git push origin feature/login

리베이스 vs 백머지

개념 비교

항목리베이스 (Rebase)백머지 (Back Merge)
목적feature 브랜치를 최신 main 위에 깔끔히 다시 얹기main의 내용을 feature 브랜치로 가져오기
동작 방식커밋을 재배치해서 히스토리 수정main을 그대로 합쳐서 merge commit 추가
히스토리일자로 깔끔merge commit 때문에 복잡
위험성히스토리 재작성 → 신중해야 함안전함
브랜치 push강제 push 필요 (--force-with-lease)일반 push 가능

실전에서는?

상황권장
히스토리를 정갈하게 유지하고 싶은 팀리베이스
충돌이 많아 위험하거나 파일이 큰 경우백머지
Git 초반에 익숙하지 않은 팀백머지
PR 전 마지막 정리리베이스
다른 팀원이 같은 브랜치에 자주 push함백머지

결론

리베이스 = 깔끔한 대신 세심함 필요
백머지 = 안전하고 단순하지만 히스토리 지저분해짐


📝 암호화 및 구글 로그인

기존에 만든 투두앱

문제점

모든 문서에 ‘주인’ 정보가 없음
각 문서에 어떤 사용자가 작성한 것인지 식별할 수 있는 작성자 정보가 없음
그래서 누가 어떤 할 일을 추가했는지 구분이 안 되고, 수정/삭제도 애매

해결책

각 문서에 주인에 대한 정보를 넣기
어떻게? → 구글 로그인을 통해 인증된 사용자의 고유 ID(uid) 를 활용

대칭키 / 비대칭키 암호화

대칭키 암호화

하나의 공통 키로 암호화와 복호화를 모두 수행
빠르지만, 키가 유출되면 모든 정보가 털릴 수 있음

비유

집 열쇠 한 개로 문을 잠그고 열 수 있는 것

민수가 친구에게 비밀 메시지 “안녕”을 보내고 싶음.
민수와 친구가 같은 비밀번호 ‘1234’를 알고 있음
민수가 메시지를 ‘1234’로 암호화 → 암호문: xYz!
친구가 같은 ‘1234’로 복호화 → 원래 메시지 “안녕”

특징

장점: 암호화/복호화 속도가 빠름
단점: 키를 안전하게 전달해야 함 → 키가 유출되면 보안 끝

비대칭키 암호화

🔓 공개키(Public Key)로 암호화
🔒 개인키(Private Key)로만 복호화 가능
키가 쌍으로 존재하므로, 보안성이 더 높음

비유

  • 공개키: 자물쇠를 누구나 사용할 수 있게 배포
  • 개인키: 그 자물쇠를 열 수 있는 유일한 열쇠

친구가 자물쇠를 만들어서 민수에게 줌 (공개키)
민수는 그 자물쇠에 편지 넣고 잠금 → 이제 아무도 열 수 없음
친구만 자신의 열쇠(개인키)로 자물쇠를 열어 편지 확인

민수는 자물쇠를 잠글 수 있지만, 열 수는 없음.
반대로 친구는 열 수 있지만, 잠그는 데 쓰는 공개키는 이미 누구나 알 수 있음

비대칭키를 활용한 전자서명

보내는 사람: 개인키(Private Key)로 서명
받는 사람: 공개키(Public Key)로 서명 검증
중요한 점: 서명은 메시지를 직접 암호화하는 것이 아니라, 보통 메시지의 해시값을 암호화해서 만듬
→ 속도 빠르고 안전함

비유

민수가 친구에게 편지를 보내고 싶음
편지 봉투에 도장을 찍는 것이 서명이라고 생각
민수는 자기 도장(개인키)을 편지 봉투에 찍음 → 이 도장은 민수만 찍을 수 있음
친구는 민수가 공개한 도장 모양(공개키)을 확인
도장이 맞으면: “아, 이 편지는 정말 민수가 보낸 거구나!”
편지가 바뀌었으면: 도장이 안 맞음 → 변조 확인 가능

개인키: 도장 찍는 사람만 가지고 있음 → 서명 생성
공개키: 누구나 볼 수 있음 → 서명 검증
메시지 내용이 바뀌면 도장이 맞지 않음 → 안전하게 변조 확인 가능

구글로그인에서 활용

비대칭키의 전자서명방식을 로그인에서도 사용
앱에 구글 ID, PW를 보내면 앱에서 나쁜짓을 할 수 있기 때문

flowchart
	U[User📱]
	GA[Google Auth Server]
	GDB[(Google Auth DB)]
  FA[Firebase Authentication]
  FDB[(Firebase Authentication DB)]
	U --1️⃣ 로그인 시도--- GA
	subgraph Google Server
	GA <--2️⃣ 아이디 비밀번호 데이터에비스의 정보와 비교해서 검증---> GDB
  end
  GA --3️⃣ 사용자를 특정지을수 있는 Token 발급--- U
	U --4️⃣ Firebase Authentication 서버로 Google Token 전송--- FA
	FA <--5️⃣ Google Server  Token 검증요청---> GA
	subgraph Firebase
	FA <--6️⃣ 검증  회원정보 없으면 회원정보 생성, 있으면 기존의 정보로 Firebase Token 생성---> FDB
  end
  FA --7️⃣ Firebase Token 전송---U

📝 Firebase 구글 로그인

Firebase 구글 로그인 활성화

파이어베이스 프로젝트에서
구글에서 자신의 개인키로 잠근 상자를 열 수 있게 하는 설정

  1. Firebase 웹 콘솔에서 Authentication 클릭

  2. 시작하기 클릭

  3. 구글 클릭

  4. 사용 설정 후 저장

  5. Flutter 프로젝트 터미널에서 파이어베이스 재구성해주기

flutterfire configure

? You have an existing firebase.json file and possibly already configured your project for Firebase. Would you prefer to reuse the values in your existing firebase.json file to configure your project? (y/n) › yes

여기서 반드시 no 입력
yes 시 내 PC에 있는 구성으로 재설정하는거라 무용지물

firebase_options.dart already exists, do you want to override it?

여기서는 yes

안드로이드 debug.keystore 등록

Keystore = "앱 서명용 열쇠 보관함"
Android 앱은 서명된 상태로만 설치/실행될 수 있음
누가 만들었는지 확인하는 용도로 사용되고 악의적인 사용자가 만들었는지 검증할 때 사용
이때 사용하는 서명 키를 저장해두는 공간이 바로 keystore

디버그 모드에서는?

개발 중 앱을 실행할 때는 매번 키 만들고 서명하기 번거로움
그래서 Android는 기본적으로 아래 경로의 기본 keystore를 사용해서 자동으로 서명

# 맥
사용자폴더/.android/debug.keystore
# 윈도우
사용자폴더\.android\debug.keystore

비밀번호도 고정
storepass: android
keypass: android

Firebase에서는 앱이 어떤 키로 서명됐는지를 보고 “이건 진짜 너희 앱이구나”를 판별
→ 이 요청이 정말 등록된 앱에서 왔는지 판별

등록

  1. 인증서 지문 확인
  - 디버그 키 비밀번호는 `android`
  - Mac : `keytool -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore`
  - Windows : `keytool -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore`
  1. Firebase 웹 콘솔에서 프로젝트 설정 클릭
  2. 디지털 지문 추가

패키지 추가

google_sign_in : 구글 계정으로 로그인하는 구글 로그인 전용 패키지. Firebase에 보낼 잠긴상자(JWT) 받아오기 위한 패키지

  • 프로젝트 터미널에서 flutter pub add google_sign_in:^6.0.0
    - 아직 7.0.0 이상 버전은 안정화가 되지 않았기 때문에 6.0.0 이상 7.0.0미만 버전 사용

firebase_auth : Google 로 부터 받은 상자(JWT)를 Firebase에 보내기 위한 패키지

  • 프로젝트 터미널에서 flutter pub add firebase_auth

google_sign_in 패키지 설정

Android는 추가로 설정해주어야 할 내용이 없지만 iOS는 설정해주어야 할 내용이 있음
ios/Runner/Info.plist 에서 아래의 내용 추가

  • 앱의 기본 정보와 권한 설정 등을 담고 있는 설정 파일

구글 로그인 시 어떤 앱애서 로그인하는지 알려주는 역할

	<key>GIDClientID</key>
	<string>[YOUR IOS CLIENT ID]</string>
  • 브라우저 혹은 구글앱에서 로그인하고 우리앱으로 돌아올 수 있게 하는 설정
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>[REVERSED_CLIENT_ID]</string>
			</array>
		</dict>
	</array>
	<key>GIDClientID</key>
	<string>[YOUR IOS CLIENT ID]</string>
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>[REVERSED_CLIENT_ID]</string>
			</array>
		</dict>
	</array>

ios/Runner/GoogleService-Info.plistCLIENT_IDREVERSED_CLIENT_ID를 각각 Info.plist에 복사

📝 투두 앱에 구글 로그인 적용하기

Login 구현

로그인 페이지를 만들어 앱의 시작점을 LoginPage로 바꾸기

기본 코드 흐름

  // ✅ 1. 구글 로그인 기능을 제공하는 기본 객체 생성
  final GoogleSignIn googleSignIn = GoogleSignIn();

  // ✅ 2. 사용자에게 Google 계정 선택 UI를 띄우고 로그인 시도
  // → 로그인에 성공하면 구글 계정 정보가 담긴 객체가 반환됨
  // → 실패하거나 사용자가 취소하면 null
  final GoogleSignInAccount? googleSignInAccount =
      await googleSignIn.signIn();

  // ✅ 3. 로그인된 계정에서 토큰 정보(accessToken, idToken)를 가져옴
  final GoogleSignInAuthentication? googleSignInAuthentication =
      await googleSignInAccount?.authentication;

  // ✅ 4. 로그인 실패 (토큰을 못 가져온 경우) 처리
  if (googleSignInAuthentication == null) {
    return; // 로그인 실패로 처리
  }

  // ✅ 5. Firebase Auth에서 사용할 OAuthCredential 생성
  // → Firebase 인증에서 사용 가능한 자격 증명 객체
  final OAuthCredential oauthCred =
      GoogleAuthProvider.credential(
        accessToken: googleSignInAuthentication.accessToken,
        idToken: googleSignInAuthentication.idToken,
      );

  // ✅ 6. Firebase에 로그인 요청
  // → Firebase에 로그인한 결과 반환 (성공 시 user 객체 포함)
  final UserCredential userCredential = await FirebaseAuth
      .instance
      .signInWithCredential(oauthCred);

  // ✅ 7. 로그인된 유저의 UID 출력
  // → 로그인한 Firebase 사용자의 고유 식별자 출력
  print(userCredential.user?.uid);

LoginPage 의 버튼 터치 함수에서 구현

    onPressed: () async {
      final GoogleSignIn googleSignIn = GoogleSignIn();
      final googleSignInAccount = await googleSignIn.signIn();
      final googleSignInAuthentication =
          await googleSignInAccount?.authentication;

      if (googleSignInAuthentication == null) {
        return; // 로그인 실패로 처리
      }

      final oauthCred = GoogleAuthProvider.credential(
        accessToken: googleSignInAuthentication.accessToken,
        idToken: googleSignInAuthentication.idToken,
      );

      final userCredential = await FirebaseAuth.instance
          .signInWithCredential(oauthCred);
      if (userCredential.user != null) {
        Navigator.pushReplacement(
          context,
          MaterialPageRoute(
            builder: (context) {
              return HomePage();
            },
          ),
        );
      }
    },

pushReplacement
현재 페이지를 제거하고 새 페이지로 이동
뒤로가기 시 이전 페이지로 돌아갈 수 없음

메서드특징
push새 페이지를 쌓음, 뒤로가기 가능
pushReplacement현재 페이지 제거 후 이동

Firestore 연동

  1. 투두 저장 시 uid 함께 저장하기
'uid': FirebaseAuth.instance.currentUser!.uid
  1. 내 투두 리스트만 가져오기
    where: Firestore 컬렉션에서 문서를 조건에 따라 필터링하기 위한 메서드
요소설명
'fieldName'조건을 걸 필드 이름 (문서 내 key)
isEqualTo:비교 연산자 (그 외: isGreaterThan, isLessThan, arrayContains 등)
someValue비교할 값

재실행 하게되면 아래와 같이 콘솔창에 에러 출력. 링크 클릭해서 색인 만들어줘야함

  • Firestore는 컬렉션 내 수많은 문서 중에서,
    - 각 문서의 uid 필드를 꺼내보고
    - 거기에 지정된 값(isEqualTo)이 있는지 비교한 후 일치하는 문서만 반환함

단순 조건이라면 색인을 자동으로 생성하지만,
복합 조건(where + orderBy)는 직접 색인 생성 필요함.
→ 빠른 검색용 사전

공부 소감

git이랑은 조금 친해진 것 같다 조오금..
내일 팀원들 작업한 거 Merge해보면서 더 익혀보는 걸로
아무튼 구글 로그인 구현한 거 증맬 뿌듯하다~

0개의 댓글