project

직심: 앱인토스에서 서비스 운영하기

목차

  1. 왜 앱인토스에서 직심을 만들었을까?
  2. 좋아요 기능 마이그레이션과 Edge Function 재배포
  3. CORS preflight에서 PATCH가 차단됐다
  4. 피드와 상세 API를 다시 설계하기
  5. RPC를 사용하면서 책임 나누기
  6. 큐잉: 바로 끝내지 않고 이어지는 일
  7. iOS WebView에서 스크롤과 Drawer의 선택
  8. eCPM을 높이기 위한 광고 조회와 수익화

왜 앱인토스에서 직심을 만들었을까?

이번 프로젝트에서 앱인토스를 진행한 이유는 AI로 만든 앱이 단순히 개인 프로젝트에 머무르지 않고, 실제 제출과 사용자의 영역에 들어갈 수 있는 경험이 되기를 바라서였다. 개인 화면에서 동작하는 것과 누군가가 실제로 사용할 수 있는 앱이 되는 것 사이에는 큰 차이가 있다고 느꼈다. 특히 앱은 사용자의 동선, 광고, 백엔드, 데이터베이스, 실제 기기까지 함께 연결되어야 하기 때문에 그 차이를 직접 경험하고 싶었다.

그래서 이번 앱인토스 프로젝트에서 직심을 만들면서 AI에게 상당 부분의 구현을 맡겼다. 기능 구현뿐 아니라 제출 전까지의 완성도와 실제로 사용할 수 있는 구조를 만드는 과정까지 포함하려고 했다. 단순히 화면을 빠르게 만드는 것에서 끝나지 않고, 사용자가 실제로 찾고 사용할 수 있는 앱으로 만들려면 무엇이 필요한지 확인하는 것이 이 프로젝트의 출발점이었다.

이 포스팅에서는 내가 운영을 하면서 도움이 될 만한 경험을 기능별로 공유하겠다.


좋아요 기능 마이그레이션과 Edge Function 재배포

좋아요 기능을 추가하는 일은 처음에는 단순한 추가 작업처럼 보였다.

기존 데이터에 좋아요를 저장 할 수 있도록 테이블과 관계를 만들고, 화면에서 누를 수 있도록 API와 상태 관리를 연결하면 되는 작업처럼 보였기 때문이다. 하지만 실제로는 이미 동작하는 서비스에 기능을 추가하는 것이라서 고려해야 할 것이 훨씬 많았다.

스키마만 만들고 끝나지 않기

좋아요 데이터가 만들어졌다고 기능이 끝난 것은 아니다. 기존 데이터와 새 데이터를 함께 고려해야 하고, 프론트엔드와 서버 라우트, 데이터베이스 함수까지 같은 상태로 맞추어야 한다.

내가 이 부분에서 중요하게 생각한 것은 아래와 같다.

  • 좋아요를 누른 사용자와 대상 데이터를 구분해서 저장하기
  • 같은 사용자가 같은 대상을 중복해서 좋아요하지 못하게 만들기
  • 좋아요 수와 좋아요 여부를 화면에서 어떻게 계산할지 정하기
  • 이미 만들어진 데이터가 새 구조와 맞지 않을 때 어떻게 처리할지 정하기
  • 마이그레이션 이후 실제 데이터베이스에 반영됐는지 확인하기

AI에게 스키마를 요청하면 비교적 빠르게 SQL 초안을 만들 수 있었다. 하지만 그 SQL이 지금 운영 중인 데이터에 바로 적용 가능한지는 내가 확인해야 했다. 기존 테이블의 이름이나 관계가 조금만 달라도 코드에서는 컴파일이 되더라도 실제 실행은 실패할 수 있기 때문이다.

배포된 코드와 로컬 코드는 다르다

좋아요 요청을 처리하는 Supabase Edge Function을 사용하면서 또 하나 배운 것이 있었다.

로컬에서 수정한 함수가 배포된 함수에도 반영됐는가?

이 문제는 언제나 터질 수 있는 부분이라고 느꼈다. 함수 코드를 수정했다고 해서 실제 서비스에서 호출되는 코드가 자동으로 바뀌는 것은 아니었다. 배포를 다시 하고, 호출 경로가 맞는지 확인하고, 필요한 환경 변수가 설정되어 있는지 확인해야 했다.

그래서 내가 관리한 순서는 대략 이랬다.

  1. 로컬에서 함수 호출 확인
  2. 마이그레이션 적용 여부 확인
  3. Edge Function 다시 배포
  4. 실제 요청의 상태 코드 확인
  5. 함수 로그에서 실행 과정 확인
  6. 클라이언트에서 화면과 서버 상태 일치 여부 확인

이 과정에서 단순히 기능을 추가하는 것보다, 변경된 코드가 어디까지 반영되는지 확인하는 것이 더 중요하다는 걸 배웠다. 특히 서버리스 환경에서는 코드가 여러 곳에 나뉘어 있으므로 배포 단계를 하나의 기능으로 이해하는 습관이 필요하다.


CORS preflight에서 PATCH가 차단됐다

직심에서 데이터를 다룰 때 GET과 POST는 큰 문제 없이 동작했다. 데이터를 읽어오고 새로운 데이터를 만드는 과정은 비교적 익숙했다.

반면 PATCH 요청은 CORS preflight 단계에서 막혔다. 일반적인 PATCH 처리 오류가 아니라, 브라우저가 먼저 보내는 OPTIONS 요청에서 PATCH 메서드가 허용되지 않아 실제 PATCH 요청이 서버에 도달하지 않은 것이 원인이었다.

왜 GET이나 POST가 되고 PATCH는 실패했을까?

브라우저는 일부 요청을 서버로 보내기 전에 OPTIONS preflight를 먼저 보낸다. 이 과정에서 서버는 요청을 실제로 처리하는 대신, 사용할 수 있는 메서드와 헤더를 브라우저에 알려줘야 한다.

PATCH 요청이 실패했을 때 초점은 PATCH 본문이나 RLS 정책이 아니라 OPTIONS 응답에 있었다. 서버가 PATCH 본문을 요구하는 Content-Type이나 인증 관련 헤더를 허용했지만, Access-Control-Allow-Methods에는 PATCH가 빠져 있었던 것이다.

그 결과 브라우저는 서버가 PATCH를 허용하지 않는다고 판단하고 실제 PATCH 요청을 보내지 않았다. 개발자 도구에서 OPTIONS 요청이 실패한 뒤 PATCH 요청 자체가 표시되지 않는 경우가 이 문제의 단서였다.

수정 방향은 서버의 CORS 설정에서 OPTIONS와 PATCH를 함께 허용하는 것이었다.

  • OPTIONS 요청을 정상적으로 처리하는지 확인
  • Access-Control-Allow-Methods에 PATCH를 포함하는지 확인
  • Access-Control-Allow-Headers에 PATCH 요청에서 사용하는 헤더를 포함하는지 확인
  • 실제 PATCH 요청과 응답에 접근 제어 헤더가 포함되는지 확인

이 흐름을 정리하면 다음과 같다.

브라우저 → OPTIONS preflight → PATCH가 허용되지 않음 → 실제 PATCH 요청 차단
브라우저 → OPTIONS preflight → PATCH 허용 → 실제 PATCH 요청 실행

따라서 OPTIONS 요청이 실패하고 뒤따르는 PATCH 요청이 보이지 않는다면 데이터베이스나 PATCH 처리 로직보다 CORS preflight에서 PATCH가 허용되는지 먼저 확인해야 한다.

디버깅의 순서를 정하기

이번 프로젝트에서 내가 얻은 가장 실용적인 습관은 다음과 같다.

  1. 화면이 이상하다는 사실을 기록한다.
  2. 브라우저 개발자 도구에서 OPTIONS와 PATCH 요청을 각각 확인한다.
  3. PATCH 요청이 아예 전송되지 않았다면 CORS preflight부터 확인한다.
  4. 상태 코드와 응답 헤더에서 허용된 메서드와 헤더를 확인한다.
  5. OPTIONS가 통과한 뒤에도 실패하면 서버의 PATCH 처리 로직을 확인한다.
  6. 요청 본문, 대상 데이터, RLS 정책을 확인한다.
  7. 마지막으로 UI 상태를 다시 확인한다.

화면에서 보이는 오류만 따라가면 어디가 문제인지 추측하기 쉽다. 하지만 OPTIONS와 실제 PATCH 요청을 분리해 확인하면 문제가 CORS 단계인지 서버 처리 단계인지 분명하게 구분할 수 있었다.


피드와 상세 API를 다시 설계하기

좋아요 문제를 해결하면서 한 가지 더 생각하게 된 부분이 있었다. 피드와 상세를 각각 어떻게 구성해야 화면이 자연스러워지고, 서버가 불필요한 일을 하지 않게 될까 하는 것이었다.

처음에는 화면에 필요한 데이터를 하나씩 요청했다. 하지만 피드에서 게시물 정보, 사용자 정보, 좋아요 수, 현재 사용자의 좋아요 여부를 각각 따로 가져오게 되면 요청이 늘어나고, 어떤 요청이 늦게 도착했을 때 화면 상태가 어긋날 수 있다.

피드는 get_feed RPC 하나로

피드 API를 정리하면서 get_feed RPC 하나를 두고, 피드에 필요한 데이터를 한 번에 묶어 전달하도록 했다.

정리한 흐름은 다음과 같다.

클라이언트 → 서버 라우트 → get_feed RPC → 피드 응답

이렇게 하면 서버 라우트가 Supabase의 여러 테이블을 직접 조합하는 대신 하나의 RPC를 호출하고, RPC가 필요한 데이터를 반환하도록 할 수 있다. RPC가 반드시 더 좋은 것은 아니지만, 피드처럼 여러 데이터가 결합된 조회에는 하나의 명확한 진입점이 생긴다.

get_feed을 만들면서 다음을 고민했다.

  • 피드의 정렬 기준은 무엇인가?
  • 같은 시간이거나 정렬 기준이 없는 데이터는 어떻게 구분하는가?
  • 커서 페이지네이션을 사용할 것인가?
  • 현재 로그인한 사용자의 좋아요 여부는 어떻게 포함하는가?
  • 게시물이 삭제되었거나 접근 권한이 없는 경우는 어떻게 처리하는가?
  • 데이터가 많을 때 전체를 가져오지 않고 제한할 수 있는가?

결국 RPC는 단순히 SQL을 숨겨 놓는 기능이 아니라, 데이터베이스가 어떤 결과를 보장해야 하는지 정의하는 경계라는 생각이 들었다. RPC의 책임이 커질수록 함수 안의 권한 설정과 반환 데이터의 형태가 중요해진다.

상세도 하나의 응답으로

상세 화면도 좋아요 정보를 별도 요청하지 않고 하나의 상세 응답에 포함시키는 방향으로 정리했다.

GET /feed
GET /posts/:id

컨셉상으로는 이렇게 흐름을 단순하게 만들었다. 클라이언트가 상세 데이터를 가져온 뒤 좋아요 API를 다시 호출하지 않도록 하는 것이다.

이렇게 하면 좋아요 UI와 서버의 실제 상태를 맞추기 쉬워지고, 여러 요청의 완료 순서에 따라 화면이 달라지는 상황도 줄일 수 있다. 상세 API 하나가 너무 많은 데이터를 모두 책임져야 한다는 점은 남아 있지만, 피드와 상세를 맞추는 과정에서 API의 목적을 다시 생각하게 되었다.

API 개수를 줄이는 것만은 목표가 아니다

요청 수를 줄이는 것도 중요하지만, 결국 더 중요한 것은 클라이언트와 서버가 같은 데이터 계약을 이해하는 것이다.

필드 이름 하나가 달라지고, 좋아요 수가 항상 정수라고 가정하고, 권한이 없는 좋아요 정보가 빠져 있다고 생각하면 화면 전체가 조용히 잘못될 수 있다. 그래서 API를 정리하면서 응답 데이터의 형태와 예외 상황까지 함께 생각하는 편이 좋았다.


RPC를 사용하면서 책임 나누기

RPC를 사용하면서 책임을 나누는 것도 배울 수 있었다.

처음에는 하나의 함수에 가능한 많은 로직을 넣으려고 했다. 피드를 가져오고, 사용자 정보를 붙이고, 좋아요 여부를 계산하고, 게시물을 정렬하는 작업을 한 함수에서 모두 처리하려 한 것이다.

하지만 이렇게 하면 함수가 담당하는 범위가 커지고, 왜 어떤 필드가 빠졌는지 찾기 어려워진다. 결과적으로 다음처럼 책임을 나누는 것이 낫다는 생각이 들었다.

  • 데이터베이스가 보장해야 하는 조회와 집계
  • 인증과 권한에 대한 정책
  • 서버 라우트의 입력 검증과 응답 변환
  • 클라이언트의 상태 관리와 화면 표시

특히 RPC는 서버 라우트보다 데이터베이스에 가까워서 보안에 대한 기대가 크다. 하지만 RPC를 만든다고 권한 문제가 자동으로 해결되는 것은 아니다. 누가 어떤 행을 읽을 수 있고, 누가 어떤 데이터를 변경할 수 있는지 여전히 명확하게 정의해야 한다.

읽어보기 : RPC와 REST의 차이점은 무엇인가요?


큐잉: 바로 끝내지 않고 이어지는 일

좋아요와 피드를 작업하면서, 어떤 일은 요청이 끝난 뒤 바로 처리해야 하고 어떤 일은 나중에 처리해도 된다는 생각이 들었다.

즉시 처리해야 하는 일은 사용자가 버튼을 눌렀을 때 결과를 바로 보여줘야 하는 작업이다. 반대로 사용자가 결과를 기다릴 필요가 없는 일은 큐에 넣어서 나중에 처리할 수 있다.

개념적으로는 다음 흐름이 된다.

클라이언트 → API → 데이터 저장 → 큐에 작업 추가 → 작업 처리자가 처리

큐잉을 하면 API가 빨리 끝나고, 하나의 요청에서 너무 많은 작업을 처리하지 않아도 된다. 하지만 큐를 사용한다고 해서 단순해지는 것은 아니다. 오히려 다음 문제를 새롭게 고려하게 된다.

  • 큐에 작업이 추가되었는지는 어떻게 확인하는가?
  • 작업이 실패하면 다시 시도하는가?
  • 같은 작업이 여러 번 실행되어도 안전한가?
  • 결과가 반영되기 전까지 화면은 어떤 상태인가?
  • 큐에 작업이 쌓이면 어떻게 확인하는가?
  • 오래된 작업을 어떻게 정리하는가?

특히 재시도와 중복 실행을 생각하지 않으면 같은 좋아요 요청이 여러 번 처리될 수 있다. 이때는 단순히 큐를 붙이는 것이 아니라 멱등성을 설계해야 한다. 하나의 작업이 여러 번 도착해도 최종 결과가 잘못되지 않도록 만드는 것이 중요했다.

또한 큐에 넣는 단계와 실제로 처리하는 단계를 구분하면, API는 성공했지만 백엔드 작업이 실패한 경우를 따로 관찰할 수 있다. 사용자에게는 문제가 없어 보이지만 데이터 집계나 알림이 반영되지 않는 상황이 생길 수 있기 때문이다.

이번 프로젝트를 통해 큐잉은 비동기 처리만의 문제가 아니라, 상태와 실패를 관리하는 문제라는 걸 배웠다. 큐에 넣었다는 사실만으로 시스템이 정상이라고 생각하면 안 된다. 실제로 처리됐는지, 실패했을 때 무엇을 할지까지 포함해야 한다.


iOS WebView에서 스크롤과 Drawer의 선택

이번에는 iOS 앱 안에서 WebView로 화면을 보여주는 부분도 맞닥뜨렸다. 기능 자체는 어느 정도 구현되었지만, 실제 기기에서 보니까 스크롤과 Drawer의 움직임이 자연스럽지 않은 문제가 생겼다.

처음에는 단순히 iOS에서는 WebView의 스크롤이 다른 브라우저와 달라서 그런가 생각했다. 하지만 원인을 한 문장으로 단정하기에는 모호했다. 문제를 확인하려면 어디에서 발생했는지 나누어 봐야 한다.

  • 네이티브 앱의 컨테이너가 화면 크기와 터치를 처리하는 부분
  • WebView가 페이지의 스크롤을 처리하는 부분
  • 웹 안의 CSS와 DOM이 만드는 스크롤 영역
  • Drawer를 열었을 때 배경 화면의 스크롤을 막는 부분

한쪽에서는 페이지 전체가 스크롤되고, 다른 쪽에서는 특정 컨테이너가 스크롤되도록 만들 수 있다. 두 방식이 겹치거나, 고정된 Drawer와 스크롤 영역이 같은 이벤트에 반응하면 사용자가 기대한 것과 다른 움직임이 생길 수 있다. iOS의 관성 스크롤, 바운스, 안전 영역과 WebView의 레이아웃까지 함께 영향을 준다.

그래서 문제의 범위를 줄이기 위해 몇 가지 경우를 나눠서 확인했다.

  • 일반 브라우저에서도 같은 문제가 발생하는가?
  • iOS의 일반 브라우저에서도 같은 문제가 발생하는가?
  • 실제 앱의 WebView에서만 발생하는가?
  • Drawer가 닫혀 있을 때와 열려 있을 때 차이가 있는가?
  • 페이지 상단, 중간, 하단에서 같은 문제가 발생하는가?
  • 천천히 스크롤할 때와 빠르게 스크롤할 때 차이가 있는가?

이 과정을 통해 단순히 “iOS라서 스크롤이 이상하다”라고 넘기기보다, 어떤 계층에서 상호작용이 충돌하는지 확인하는 편이 낫다는 생각이 들었다.

Drawer로 바꾼 것은 해결책이었나, 타협이었나

스크롤 문제가 발생했다. 안드로이드에서는 발생하지 않던 문제가 IOS에서 문제가 생겼다. 결국 기존 UI를 유지하면서 문제를 해결하기보다는 IOS에서 상세 페이지 진입을 하면 Drawer 형태로도 구현한 UI에 착안하여 이를 변경했다.

Drawer가 열리면 배경 스크롤을 막고, 필요한 동작은 Drawer 내부에서만 처리하도록 했다. 적어도 기존처럼 여러 스크롤 영역이 서로 간섭하는 문제는 피할 수 있었다.

물론 이게 가장 좋은 해결책인지는 모르겠다. 문제를 해결하기 위해 UI 자체를 바꾼 것이기 때문이다. 사용자 입장에서도 기존 화면보다 Drawer가 더 편한지는 별개의 문제다. 특히 iOS WebView에서는 웹에서 자연스럽게 느껴지는 동작이 앱에서도 똑같이 자연스럽지는 않을 수 있다.

이번에는 일단 Drawer로 정리했지만, 다음에 비슷한 문제를 만난다면 CSS를 수정하기 전에 한 번 더 생각해 보려고 한다.

지금 겪고 있는 문제가 구현의 문제인지, 처음부터 UI 구조를 잘못 선택한 것인지.

이번 경험에서는 후자에 가까웠던 것 같다.


eCPM을 높이기 위한 광고 조회와 수익화

바이브 코딩으로 앱을 만든 이유 중 하나는 기능을 빠르게 구현하는 것만큼 실제로 사용할 수 있는 서비스로 만들고 수익화할 수 있는 형태로 만들기를 원했기 때문이다. 화면에 광고를 붙이는 것만으로는 수익화가 되지 않는다. 광고가 실제 사용자 흐름에서 의미 있는 노출과 클릭으로 이어지고, 앱을 사용하는 동안에도 부담이 크지 않아야 한다.

이 과정에서 먼저 알아본 개념이 eCPM이다. eCPM은 광고가 1,000번 노출될 때 얻을 수 있는 수익을 뜻한다. 광고를 많이 보여주는 것 자체가 목적이 아니라, 같은 노출 대비 어떤 광고와 어떤 위치가 더 나은 수익을 만드는지 비교하는 지표다. 따라서 광고를 많이 띄우는 것만으로 수익화가 잘된다고 볼 수 없고, 사용자가 보는 순서와 광고가 나오는 순간을 함께 고려해야 한다.

광고 로딩 흐름을 변경하다

처음에는 리워드 광고를 앱이 실행될 때 미리 요청하도록 구현했다. 하지만 이 방식에는 한 가지 문제가 있었다. 사용자가 티켓 페이지에 진입하기도 전에 광고를 요청한다는 점이다.

예를 들어 티켓 페이지에 들어왔다가 광고가 있다는 것을 확인하고 다른 페이지로 이동하면, 광고 요청은 이미 발생했지만 실제 광고 시청으로 이어지지는 않는다. 결국 광고를 보여줄 기회조차 없었던 사용자에게 불필요한 요청을 보내고 있었던 셈이다.

광고 요청 횟수가 늘어난다고 수익이 함께 늘어나는 것도 아니다. 실제 노출로 이어지지 않는 요청이 많아지면 오히려 불필요한 호출만 늘어나게 된다.

이후에는 광고 요청 횟수 자체보다 실제 광고를 시청한 사용자를 확인하는 데 집중했다. 이벤트를 연동해 인앱 광고 시청 유저를 핵심 지표로 확인할 수 있도록 했고, 광고 요청과 실제 노출이 어떻게 이어지는지도 살펴볼 수 있게 했다.

광고 요청 버튼 동작에 의해 그제서야 광고를 요청하기에 로딩시간이 좀 걸리기에 로더를 넣는 것으로 UX를 일부 해결했다. 광고가 준비되지 않았더라도 사용자의 주요 동작에는 영향을 주지 않도록 정리했다.

이번 변경을 통해 광고를 단순히 많이 요청하는 것보다 실제 사용자가 광고를 접하는 시점에 맞춰 요청하는 것이 중요하다는 점을 알게 됐다.

광고 수익은 노출 횟수만으로 결정되지 않는다. 사용자가 광고를 실제로 시청할 수 있는 흐름을 만들고, 그 과정에서 발생하는 요청과 노출을 제대로 측정하는 것부터 시작해야 했다. 광고요청에 대한 타이밍은 좀 더 연구를 해봐야겠다.

eCPM을 보면서 배운 기준

이 경험을 통해 eCPM을 높이는 일은 광고를 무조건 많이 띄우는 일이 아니라는 생각이 들었다. 다음 기준을 함께 확인해야 한다는 걸 배웠다.

  • 광고가 사용자에게 노출되는 순서와 타이밍
  • 광고가 연결되는 위치와 사용자 행동
  • 광고 요청이 실제 노출과 클릭으로 이어지는지
  • 같은 광고를 중복해서 요청하지 않는지
  • 실패한 요청과 불필요한 네트워크 비용이 발생하지 않는지
  • 광고 경험이 서비스의 핵심 기능을 방해하지 않는지

결국 바이브 코딩은 기능을 빠르게 만드는 것에서 끝나지 않는다. 사용자가 실제로 찾고 사용하는 구조를 만들고, 그 구조 위에 수익화가 지속될 수 있는지 생각하는 단계까지 포함해야 한다. eCPM은 숫자 하나가 아니라 광고와 사용자, 서비스 구조가 어떻게 맞는지 확인하는 지표였다.


마치며

이번 프로젝트에서 AI가 만든 기능들은 정말 빠르게 추가할 수 있었다. 하지만 빠르게 추가하는 것과 이해하면서 유지하는 것은 다른 문제였다.

좋아요 기능 하나를 추가하면서 마이그레이션과 Edge Function 배포를 다시 확인했고, PATCH 요청이 실행되지 않은 원인이 일반적인 요청 오류가 아니라 CORS preflight에서 PATCH가 차단된 문제였다는 것도 배웠다.

피드와 상세 API를 정리하면서 RPC의 경계를 찾았고, 큐잉을 통해 비동기 작업의 책임과 실패 처리를 생각했다.

그리고 iOS WebView에서는 코드가 정상적으로 보여도 사용자의 상호작용이 자연스럽지 않을 수 있다는 걸 배웠다. Drawer는 그 문제를 완전히 해결한 것은 아니지만, 지금 상황에서 더 나은 선택을 찾기 위한 한 번의 시도였다.

9월 15일 출시 이후 뒤돌아 보면,

  • 출시 4일차 기준, 나의 첫 목표 월 1,000₩원은 가볍게 달성 했다. (예상수익 1,318₩ / eCPM 37,768₩)

  • 일주일 뒤, 소셜 카테고리 실시간 인기 7위를 달성하기도 했다. (물론 주변 사람들의 도움이 컸을 것)

  • 출시 이후 한 달이 조금 넘어가는 오늘, 유저수는 197명이다. 예상 정산 금액으로는 3,164원.

간간히 유저들이 들어오고 있으나 리텐션이 너무 낮아 눈팅 몇 번하고는 떠나버려서 이제 간혹 커뮤니션이 되던 부분도 죽은 것 같다..

그래도 이번 프로젝트는 생각보다는 선방한 것 같다. 배운 점도 많았고, 생각보다 수익도 있었기에.. 다음번에는 이를 발판삼아 좀 더 마케팅과 리텐션에 신경써야겠다.