"[모바일프로그래밍실무] 모바일 앱 보안 완전 정리 — 네트워크 보안부터 Sentry까지"

앱 보안은 해킹 막기만이 아니에요. 운영 환경에서 에러를 잡고 추적하는 법, Sentry와 Error Boundary까지 한 번에 정리했어요.

By Noah
||8 min read|5
광고

모바일 앱 보안, 그거 그냥 로그인 암호화 아닌가요?

저도 처음엔 그렇게 생각했어요.

근데 실제로 서비스를 운영해보면, 앱 보안이라는 단어가 훨씬 더 넓은 의미라는 걸 알게 되더라고요.

앱 보안은 이 세 가지를 다 포함해요.

  • 사용자 데이터 보호 — 개인정보, 토큰, 요청 파라미터
  • 앱 안정성 유지 — 런타임 에러로 앱이 죽지 않도록
  • 문제가 생겼을 때 빠르게 찾고 고치기 — 운영 환경 관측

특히 실제 서비스에서는 개발자가 미리 테스트하지 못한 기기, OS, 네트워크 환경에서 에러가 터지기도 해요.

이걸 추적하고 분석하는 체계가 없으면, 사용자는 그냥 ★1짜리 리뷰만 남기고 앱을 삭제해버립니다. 😅

오늘은 모바일 앱 보안을 크게 두 축으로 정리해볼게요.

  1. 네트워크 보안 — MITM, HTTPS, SSL Pinning
  2. 에러 트레이싱 — Error Boundary, Sentry, Breadcrumbs

1. 네트워크 보안

중간자 공격 (MITM) 이게 뭔가요?

MITM(Man-In-The-Middle)은 공격자가 클라이언트와 서버 사이에 끼어들어 트래픽을 가로채는 공격이에요.

공격이 가능한 조건은 이렇게 다양해요.

  • 같은 공용 Wi-Fi 네트워크 (카페, 공항)
  • 악성 프록시 설정
  • 기기에 악성 루트 CA 설치

공격에 성공하면 이런 피해가 생겨요.

  • 토큰·개인정보·요청 파라미터 노출
  • 서버 응답 조작

방어책은 HTTPS 사용, 인증서 검증, cleartext 트래픽 차단이에요.


HTTPS 인증서 체인 검증

HTTPS는 서버 인증서가 신뢰할 수 있는 CA 체인에 연결되는지 확인하는 방식이에요.

흐름을 보면 이렇게 됩니다.

Root CA → Intermediate CA → Leaf Certificate → TLS Handshake → 암호화 통신

근데 여기에 한계가 있어요.

공격자가 기기에 악성 CA를 설치하면, 위조 인증서도 "신뢰할 수 있음"으로 통과해버려요.

바로 이 빈틈을 막는 게 SSL Pinning입니다.


SSL Pinning — HTTPS 위에 한 겹 더

SSL Pinning은 앱 내부에 서버의 공개키 지문을 미리 저장해두고, 연결 시 일치 여부를 추가 확인하는 방식이에요.

HTTPS를 대체하는 게 아니라 위에 추가 검증을 얹는 거예요.

핀과 일치하지 않으면 연결을 거부해요. 악성 CA가 설치돼 있어도 의미가 없어지는 거죠.

무엇을 Pinning할까?

방식설명특징
Leaf Certificate서버 인증서 전체 고정갱신마다 앱 업데이트 필요
Public Key / SPKI인증서 내 공개키 지문 고정같은 키로 재발급 시 업데이트 불필요
CA Pin중간/루트 CA 공개키 고정신뢰 범위가 넓어질 수 있음

실무에서는 Backup Pin을 함께 넣고 인증서 교체 시나리오를 미리 테스트하는 게 필수예요.

Android — Network Security Config

<network-security-config>
  <domain-config cleartextTrafficPermitted="false">
    <domain includeSubdomains="true">api.myfeed.app</domain>
    <pin-set expiration="2027-04-01">
      <pin digest="SHA-256">PRIMARY_PIN_BASE64=</pin>
      <pin digest="SHA-256">BACKUP_PIN_BASE64=</pin>
    </pin-set>
  </domain-config>
</network-security-config>

iOS — Info.plist NSPinnedDomains

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSPinnedDomains</key>
  <dict>
    <key>api.myfeed.app</key>
    <dict>
      <key>NSIncludesSubdomains</key><true/>
      <key>NSPinnedCAIdentities</key>
      <array>
        <dict>
          <key>SPKI-SHA256-BASE64</key>
          <string>PRIMARY_PIN_BASE64=</string>
        </dict>
      </array>
    </dict>
  </dict>
</dict>

Expo에서 SSL Pinning 적용하기

Expo에서 네이티브 설정을 직접 수정하면 prebuild 때 덮어써질 수 있어요.

Config Plugin으로 withAndroidManifest, withDangerousMod, withInfoPlist를 통해 자동 적용하는 게 안전해요.

그리고 네이티브 설정이 들어가는 순간 Expo Go에서는 테스트 불가예요. Development Build 또는 EAS Build가 필요해요.

⚠️ SSL Pinning 운영 리스크
인증서/키 교체 시 기존 앱이 API에 연결 못할 수 있어요.
Backup Pin으로 장애 범위를 줄이고, OTA 업데이트만으론 해결이 안 될 수 있으니 앱 바이너리 업데이트 계획도 함께 세워둬야 해요.


2. 에러 트레이싱

에러 유형 3가지

우선 에러를 종류별로 정리해봐요.

유형설명특징
Syntax Error문법 오류실행 전 감지, Metro 빌드 실패
Runtime Error런타임 오류실행 중 앱 중단, 가장 흔함
Logical Error논리 오류실행은 되지만 결과가 틀림, 가장 찾기 어려움

React Native에서 자주 발생하는 패턴은 이렇게 있어요.

  1. undefined 접근 — API 응답이 늦게 오거나 없을 때
  2. 네트워크 실패 — 오프라인, 4xx/5xx
  3. 네이티브 모듈 오류 — 권한 거부, 라이브러리 버전 불일치
  4. Render Loop — useEffect 의존성 실수
  5. 플랫폼 차이 — iOS/Android 동작 차이

console.log의 한계

개발할 때는 주로 console.log로 값을 확인하죠.

console.log(user);

근데 console.log개발 환경에서만 동작해요.

React Native에서 로그는 이렇게 전달됩니다.

JS 엔진 → Metro 번들러 → 개발자 노트북 터미널

실제 사용자 폰에는 Metro도 터미널도 없어요. 그래서 운영 환경에서는 로그를 확인할 수 없습니다.

게다가 console.log만으로는 이런 정보를 알 수가 없어요.

  • 어떤 기기인지, 어떤 OS 버전인지
  • 어떤 행동 직후에 에러가 났는지
  • 어떤 사용자에게 발생한 건지
  • Native 크래시가 나면 JS Bridge가 끊기면서 JS는 아무것도 모름

결국 운영 환경에서는 console.log만으로 문제를 추적하기가 불가능에 가깝습니다.


운영 환경에 진짜 필요한 3가지

항목설명
Remote Logging사용자 기기 로그를 서버로 전송
Crash ReportingJS + Native 크래시 자동 수집
Device ContextOS, 기기 모델, 앱 버전 등 메타데이터 첨부

이 세 가지가 있어야 실제 사용자 환경의 문제를 재현하고 분석할 수 있어요.


3. Error Boundary

앱이 하얗게 변하는 걸 막자

React에서 렌더링 중 에러가 나면 앱 전체가 흰 화면이 될 수 있어요.

이를 막기 위해 Error Boundary를 사용해요.

Error Boundary는 자식 컴포넌트 트리에서 발생한 JS 에러를 잡아내고, 앱을 멈추지 않고 대체 UI(Fallback)를 렌더링하는 React 컴포넌트예요.

한 가지 주의할 점은 Class Component로만 구현 가능하다는 거예요. 함수형으로는 못 만들어요.

class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    Sentry.captureException(error, { extra: info });
  }

  render() {
    if (this.state.hasError) return <Fallback />;
    return this.props.children;
  }
}

두 메서드 차이점

구분getDerivedStateFromErrorcomponentDidCatch
호출 시점렌더 단계 (render 이전)커밋 단계 (렌더 이후)
주 용도fallback UI 전환용 state에러 로깅·분석 전송
부수 효과금지 (순수 함수)허용

Error Boundary가 잡을 수 없는 에러

이건 꼭 알고 있어야 해요. 잡히지 않는 에러가 따로 있어요.

  • 이벤트 핸들러 (onPress 등) → try/catch + Sentry 직접 전송으로 처리
  • 비동기 코드 (async/await, setTimeout) → 이벤트 루프 큐에서 실행되므로 렌더 사이클 밖
  • Boundary 자신의 에러
  • 서버 사이드 렌더링

Expo Router에서 배치 전략

실무에서는 전역 + 부분 혼합 방식을 써요.

app/
  _layout.tsx      ← 전역 Boundary (모든 에러의 마지막 안전망)
  (tabs)/
    _layout.tsx    ← 탭 Boundary
  profile/
    _layout.tsx    ← 프로필 Boundary (위험한 영역만 격리)

부분 Boundary를 쓰면 한 화면에서 에러가 터져도 나머지 화면은 정상 작동해요.


4. Sentry — 운영 환경의 눈

Sentry가 해결하는 문제

문제Sentry 해결책
에러가 안 보임Remote Logging
원인 모름Context 자동 수집 (OS, 기기, 버전, 세션)
재현 불가Breadcrumbs — 에러 직전 행동 기록

SDK가 자동으로 수집하는 것들

  • 에러 정보 — Stack trace, 메시지, 타입
  • 디바이스 — OS, 버전, 기기 모델, 메모리
  • 앱 정보 — 버전, 빌드 번호, 환경
  • 세션/사용자 — 세션 ID, 유저 ID, 접속 시간

기본 세팅

import * as Sentry from '@sentry/react-native';

Sentry.init({
  dsn: process.env.EXPO_PUBLIC_SENTRY_DSN,
  environment: __DEV__ ? 'dev' : 'prod',
  tracesSampleRate: 0.1,
  enabled: !__DEV__,
});

export default Sentry.wrap(App);
  • dsn : Sentry 프로젝트 주소
  • environment : 개발 / 운영 환경 구분
  • tracesSampleRate : 성능 추적 샘플링 비율 (0.1 = 10%만 샘플링)
  • enabled: !__DEV__ : 운영 환경에서만 활성화

Sentry에서 제일 인상적인 기능이에요.

Breadcrumbs(빵가루) 는 에러가 터지기 전까지 사용자가 어떤 행동을 했는지 타임라인으로 기록해줘요.

Home → Profile 이동
↓
팔로우 버튼 클릭
↓
POST /api/follow 요청
↓
HTTP 500 발생
↓
TypeError 발생

이 기록 덕분에 재현이 안 되는 에러를 분석할 때 정말 유용해요.

자동으로 기록되는 항목이에요.

  • Navigation — 화면 전환
  • HTTP — API 요청/응답
  • UI Event — 버튼 탭 등
  • Log — 커스텀 로그

console.log vs Sentry 비교

항목console.logSentry
실행 환경개발 환경만개발 + 운영
수집 위치내 터미널Sentry 서버/Dashboard
컨텍스트값 하나OS·기기·세션·릴리즈 자동
검색·그룹핑불가동일 에러 자동 그룹·검색
용도값 찍어보기운영 에러 관측·알림

즉, console.log는 개발용 도구, Sentry는 운영 환경 관측 도구예요.


5. 운영 관점의 에러 처리 3단계

정리하면 에러 처리는 이 3단계로 접근해야 해요.

단계도구역할
예방 (Prevent)TypeScript, 옵셔널 체이닝, 테스트 코드에러 발생 자체를 막음
완화 (Mitigate)Error Boundary, try/catch, fallback UI에러 영향 범위 최소화
추적 (Track)Sentry, Breadcrumbs, Stack Trace원인 파악 및 알림

이상적인 흐름을 그려보면 이렇게 돼요.

렌더 중 에러 발생
  → Error Boundary가 잡음
  → Sentry로 전송 (Stack Trace + 기기 정보 + Breadcrumbs)
  → 사용자에게 Fallback UI 표시
  → 개발자 Dashboard에서 원인 추적
  → 다음 배포에서 수정

정리

오늘 배운 걸 한 줄로 압축하면 이렇게 돼요.

앱 보안 = 막는 것 + 잡는 것 + 추적하는 것

  • 네트워크 보안: HTTPS로 암호화하고, SSL Pinning으로 한 겹 더 검증
  • 에러 완화: Error Boundary로 흰 화면을 막고 Fallback UI 제공
  • 에러 추적: Sentry + Breadcrumbs로 운영 환경에서 무슨 일이 있었는지 파악

앱을 개발할 때 기능 구현에서 멈추지 않고,

"운영 환경에서 어떤 문제가 생길 수 있을까?"

"어떻게 관측하고 대응할까?"

이 두 가지를 함께 고민하는 습관이 중요한 것 같아요. 😊

저자 소개

DevOps Engineer. 금융과 여행에 관심이 많습니다.

독자 의견

Comments (0)