풀스택 개발자 과정 95일차

너구·2026년 10월 1일

풀스택 성장과정

목록 보기
97/106

개인프로젝트

📌 오늘 한 일 한눈에 보기

구분내용
새 기능앱 결제, 앱 관리자 콘솔, 푸시 알림(FCM), 앱 소셜 로그인
추가 기능변호사 승인 취소, 관리자 → 변호사 1:1 메시지
버그 수정결제 탭 500 에러, 팝업 빨간 화면, 간헐적 테스트 실패
개선환불 문구 정리 ("결제 취소" → "바로 환불")

1. 앱 결제 (토스페이먼츠)

앱에서도 웹과 똑같이 결제할 수 있게 했다.

상품

  • 등기부 분석 이용권: 무료 5회를 다 쓰면 1회 990원
  • 원하는 변호사 직접 선택: 1회 2,900원 구현 방식
  • 토스 결제창을 앱 안의 WebView로 띄운다.
  • 결제가 끝나 돌아오는 주소를 앱이 가로채서 서버에 승인을 요청한다.
  • 금액은 서버가 자기 주문과 대조해서 검증한다. 앱이 보낸 금액을 그대로 믿지 않는다.
  • 결제 중 카드사 앱을 여는 안드로이드 intent:// 주소는 일반 링크처럼 열리지 않아서, 앱 주소로
    바꿔 여는 처리를 넣었다.

환불 정책

이용권 상태버튼처리 방식
아직 안 씀바로 환불관리자 승인 없이 즉시 환불
이미 씀환불 요청사유를 적어 요청 → 관리자가 승인 또는 거절

💡 처음에는 즉시 환불 버튼 이름이 "결제 취소"였다.
동작은 맞았지만 사용자가 환불 버튼인지 알아보기 어려워서, 문구를 전부 "환불" 기준으로 바꿨다.


2. 앱 관리자 콘솔

웹 관리자 페이지를 앱 하단 탭 4개로 옮겼다.

  • 변호사 승인: 자격 서류(PDF·JPG)를 휴대폰 기본 앱으로 열어 보고 승인하거나 거절
  • 신고 관리: 신고된 대화 원문을 확인하고 이용 정지 또는 기각
  • 회원 관리: 회원과 변호사를 검색하고 이용 정지·해제
  • 결제 관리: 환불 요청 승인·거절

    웹·앱 공통으로 추가한 기능

    ① 변호사 승인 취소
  • 누르면 바로 처리되지 않고, "정말로 ○○○ 변호사의 승인을 취소하시겠습니까?" 확인창을 먼저
    띄운다.
  • 취소하면 거절이 아니라 승인 대기로 되돌린다. 나중에 다시 승인할 수 있게 하기 위해서다.
  • 그 변호사는 즉시 로그아웃되고, 새 상담 배정에서도 빠진다. ② 관리자 → 변호사 1:1 메시지
  • 관리자가 변호사 한 명을 골라 안내 메시지를 보낸다.
  • 변호사는 알림함의 안 읽음 뱃지와 앱 푸시로 알 수 있다.
  • 관리자 화면에서는 메시지마다 읽었는지, 언제 읽었는지가 보인다.
  • 운영 안내용이라 답장은 할 수 없는 단방향으로 만들었다.

3. 푸시 알림 (Firebase Cloud Messaging)

웹은 브라우저 웹 푸시를 이미 쓰고 있었고, 앱에는 FCM을 붙였다.

받는 사람알림 내용
회원변호사 답장
변호사새 문의, 회원 메시지, 관리자 메시지
  • 알림을 누르면 해당 대화방(또는 알림함)이 바로 열린다.

  • 앱을 보고 있는 중이면 시스템 알림 대신 화면 아래 알림줄로 보여준다.

  • 로그아웃하면 그 기기 토큰을 지워서 더 이상 알림이 오지 않는다.

  • 서버는 Firebase Admin SDK 대신 HTTP v1 API를 직접 호출한다. OCR 때문에 이미 쓰던 구글 인증
    라이브러리를 재사용해서 의존성이 늘지 않았다.

  • Firebase 설정 파일이 없어도 앱은 빌드되고 푸시만 꺼지게 만들었다. 팀원 누구나 Firebase 없이
    개발할 수 있다.

  • iOS는 푸시 권한과 백그라운드 알림 설정을 프로젝트에 반영해 두었다. 실제 확인은 Mac에서 해야
    한다.


    4. 앱 소셜 로그인 (구글·카카오·네이버)

    앱 전용 SDK를 쓰려면 각 사 개발자 콘솔에 앱을 따로 등록해야 한다.
    그래서 웹의 소셜 로그인 흐름을 앱 안의 브라우저 탭으로 재사용했다.

    🔐 문제: 앱으로 돌아오는 주소는 가로챌 수 있다

    로그인 후 앱으로 돌아올 때 쓰는 projectguard:// 같은 주소는 다른 앱도 등록할 수 있다.
    세션 토큰을 그대로 실어 보내면 탈취될 위험이 있다.

    ✅ 해결: PKCE 방식 적용

  1. 앱이 비밀값(verifier)을 만들고, 그 해시값만 웹에 넘긴다.
  2. 로그인이 끝나면 웹은 세션 대신 2분짜리 1회용 코드만 앱으로 보낸다.
  3. 앱은 코드 + 처음 만든 비밀값을 서버에 보내 세션을 받는다. → 다른 앱이 코드를 가로채도 비밀값이 없으니 로그인할 수 없다.

🐛 고친 버그들

1) 관리자 결제 탭 500 에러

  • 증상: "환불 요청", "환불 완료" 탭을 열기만 해도 500 에러가 났다. 처음엔 데이터가 없어서인 줄
    알았다.
  • 원인: H2 DB가 상태(enum) 칸을 처음 만들 때의 값 3개(READY, PAID, FAILED)만 받도록 굳어
    있었다. 나중에 추가한 REFUNDED 같은 값은 조회만 해도 거부됐고, 실제 환불 저장도 실패할
    상황이었다.
  • 해결:
    • 모든 상태 칸을 일반 문자열(varchar(30))로 변경
    • Hibernate가 기존 DB 칸도 자동으로 바꿔줘서 재시작만으로 해결
    • 같은 실수를 다시 하면 잡아내는 테스트 추가

    2) 이용 정지를 누르면 앱이 빨간 화면

  • 원인: 사유 입력 팝업이 닫히자마자 입력칸을 정리했는데, 팝업은 닫히는 애니메이션 동안 그
    입력칸을 한 번 더 그렸다.
  • 해결: 팝업이 입력칸을 직접 가지고 있다가, 완전히 사라질 때 정리하도록 공용 팝업으로
    바꿨다. 같은 구조였던 가입 거절, 환불 거절, 환불 요청 팝업도 함께 해결됐다.

    3) 가끔씩만 실패하던 테스트

  • 증상: 전체 테스트를 돌리면 소켓 알림 테스트가 가끔 실패하고, 단독으로 돌리면 통과했다.
  • 원인: 처음엔 "PC가 바빠서"라고 넘겼지만, 두 번 연속 실패해서 들여다봤다. 클라이언트 연결이
    끝난 시점과 서버가 그 연결을 등록하는 시점 사이에 짧은 틈이 있었고, 그 사이에 나간 알림이
    사라지고 있었다.
  • 해결: 테스트가 서버 등록을 기다린 뒤 알림을 만들도록 수정했다.

💡 오늘 배운 점

  • "가끔 실패"에는 원인이 있다. 부하 탓으로 넘기기 전에 타이밍과 순서부터 의심하자.
  • 500 에러는 데이터 문제가 아니라 거의 항상 진짜 오류다. 추측하기 전에 로그부터 보자.
  • 자동화 테스트는 한 단계마다 화면을 확인한다. 확인 없이 터치를 연달아 보냈다가, 메뉴 크기가
    달라 엉뚱한 입력칸에 값이 들어간 적이 있다.
  • 화면 문구도 기능이다. 동작이 맞아도 버튼 이름이 헷갈리면 사용자는 기능이 없는 줄 안다.

0개의 댓글