본문으로 건너뛰기
강홍재/ James
← Work
EMBA2026 · PM · 개발· Started(First Commit date)

EMBA E-Members

성균관대 EMBA 원우들을 위한 커뮤니티 앱 - 원우수첩(인명부), 명함 QR 공유, 멘토링, 일정·게시판·식단을 하나로. 6월 양 스토어 출시 뒤 2.0 라운드에서 명함·복지·예약·도시락 키오스크·학위계산기·선거까지 넓혔고, 그 라운드는 백엔드와 어드민까지 사실상 혼자였다.

  • Flutter
  • NestJS
  • Firebase
  • iOS
  • Android
  • AWS
스크린샷
  • 마이페이지 - 학업·동호회·직장 정보를 한 곳에서 관리하고, QR 명함과 프로필을 편집한다

  • 명함 공유 - 내 QR을 상대가 스캔하면 EMBA 앱이 열리고, 미설치 시 모바일 브라우저에 명함이 표시된다

  • 식단 - 주차별로 학교 식당 메뉴를 사진과 함께 본다

Setup

Problem

EMBA 원우 네트워크는 그 자체가 자산인데, 정작 그 자산이 카톡방·엑셀 명단·종이 명함·학교 공지 메일로 흩어져 있다. "저분이 몇 기 누구였더라", "그 공지 어디 있더라", "오늘 학식 뭐지"가 매번 흩어진 채널을 뒤지는 일이 된다. 원우들에게 필요한 건 화려한 기능이 아니라 "한 곳에서 사람을 찾고, 명함을 주고받고, 일정과 소식을 놓치지 않는" 기본기다.

Context

성균관대 EMBA의 공식 원우 커뮤니티 앱. 1차 라운드(4월 30일 인수 ~ 6월 25일 출시)는 PM 1명·개발 2명의 작은 팀이었고, 제임스는 PM이자 개발자로 제품 범위·우선순위를 정의하고 인수한 Flutter 코드베이스를 개발·유지보수하며 iOS·Android 양 스토어 동시 출시까지 끌고 갔다. 2차 라운드(6월 26일 ~ 10월)는 사정이 달랐다. 외주사 백엔드 개발자의 마지막 커밋이 6월 14일이라, 이후 앱·백엔드(NestJS·MySQL)·어드민(Next.js)·명함 웹(Express)·선거 시스템 5개 레포의 커밋과 PR 225건은 전부 한 사람 몫이었다. main 기준 앱 버전은 2.0.0+39(2026-08-27)다. 다만 2.0.0이 스토어에 실제로 나갔는지는 레포에 기록이 없고, 9월 18일 시점 메모는 운영 서버가 아직 구버전을 강제 업데이트하지 않는 상태라고 적고 있다. 여기엔 확인된 것만 적는다. 원우 한 명이 끝까지 쓸 수 있는 기본기 - 사람 찾기, 명함 교환, 일정·공지 - 를 먼저 단단하게 만든 뒤, 총학생회 운영에 실제로 필요한 것(도시락 배식, 예약, 선거)으로 넓혔다.

Users

성균관대 EMBA 재학·졸업 원우, 그리고 기수 운영진.

Hypothesis

흩어진 채널 대신 한 앱에서 사람·명함·일정·소식을 처리하게 하면, EMBA 네트워크가 "필요할 때 실제로 닿는" 자산이 된다.

Build

What I did
  • PM으로 기능 범위·우선순위를 정의하고 iOS·Android 양 스토어 출시·업데이트를 운영
  • 인수한 Flutter 코드베이스를 개발·유지보수 (6월 25일 출시 뒤 main 기준 2.0.0+39, 2026-08-27)
  • 원우수첩(인명부) - 기수·직군·이름으로 검색·필터하고, 원우 프로필을 휴대폰 연락처로 바로 저장
  • 명함 QR 공유 - 상대가 스캔하면 EMBA 앱이 열리고, 미설치 시 모바일 브라우저에 명함이 표시되는 폴백
  • 멘토링 - 멘토 찾기·신청과 1:1 채팅
  • 일정 캘린더(학사·기수·개인 일정), 게시판·공지(첨부 PDF 뷰어 내장), 학교소식·웹진(웹뷰), 주차별 식단
  • FCM 푸시 알림과 KO/EN 다국어
  • 2.0 라운드(7~9월) - 명함 2종(학교 명함은 앱이 그리고 회사 명함은 업로드 이미지), 마이페이지 QR에 3분 토큰과 화면 캡처 차단, 복지·제휴 혜택과 교통 안내 탭, 총학생회 지원 이용처 예약, 학교 공지 자동 동기화(첨부는 자체 서버로 복사), 셔틀 실시간 모달
  • 도시락 배식 키오스크 - 태블릿 키오스크(/kiosk)와 조교 폰(/operator)에서 QR 토큰 검증, 회차·요일별 명부와 예비 인원, 잔여 수량을 앱에 표시, 평일 16:00 자동 개장·23:00 마감(토요일은 10:00·18:00), 명절 선물 수령은 별도 테이블
  • 백엔드(NestJS·TypeORM·MySQL)·어드민(Next.js)·명함 웹(Express)까지 직접 - 관리자 등급 300/255/233/222와 스코프 데코레이터, 로그인 시 bcrypt 지연 업그레이드, 학사일정·입학가이드 PDF 관리, 게시판 실제 페이징
  • 학위계산기(degree-planner)를 웹뷰로 임베드 - 원격 번들을 먼저 읽고 실패하면 앱 에셋으로 폴백. 계산기 데이터는 Supabase에서 EMBA 백엔드로 이관
  • 120기 기수장 선거를 위한 중앙선거관리시스템을 하루 만에 만들어 명함 웹에 얹었고(9월 17일), 같은 구조로 슬로건 선정 투표 3라운드를 돌렸다
  • 외주사 보유였던 Apple·Google Play 계정의 앱 이전(App Transfer)을 9월 4일 시작해 17일 마무리 - 개인정보처리방침·지원 페이지를 명함 웹에 신설
  • 66대 총학생회 리브랜드(2.0.0) - 색·슬로건·게시판 ID를 CouncilBrand 레이어로 분리해 다음 기수가 또 바꿀 수 있게
Product decisions
  • 기본기 우선 - 화려한 소셜 기능보다 사람 찾기·명함·일정·공지를 먼저 단단하게
  • 명함은 QR + 웹 폴백 - 상대가 앱을 안 깔았어도 명함이 전달되도록, 네트워킹 자리에서 설치를 강요하지 않음
  • 연락처는 휴대폰 기본 주소록으로 저장 - 앱을 떠나도 남는 자산
  • iOS·Android 단일 코드베이스(Flutter) - 작은 팀이 두 플랫폼을 한 번에 유지보수하도록 비용 최소화
  • QR은 3분 토큰 + 화면 캡처 차단 - 명함 QR이 도시락 체크인에도 쓰이므로 스크린샷 돌려쓰기를 막는다. 유예 기간을 두고 끝낸 뒤부터 키오스크가 토큰을 검증한다
  • 대리 수령은 만들고 같은 날 되돌렸다(8월 20일) - 1인 1식 유일성이 깨지는 걸 확인한 뒤 명절 선물은 아예 별도 테이블로 떼어 식사 유일성과 섞이지 않게 했다
  • 학교 공지 첨부는 자체 서버로 복사 - 앱이 외부 링크를 못 여는 제약을 동기화 설계에 흡수했다. 복지 출처 감시 크론은 해시 비교만 하고 쓰기는 사람이 승인한다
  • 비로그인 명함 공개 API에서 이메일은 제외 - 명함은 전달돼야 하지만 수집 가능한 연락처가 되면 안 된다
  • 제휴 상세 화면에만 흐르는 "성균관대 EMBA" 표식 - 위조 방지가 목적이라 적용 범위를 두 번 좁혔다
QA considerations
  • 인수 코드의 경계(토큰 만료·재로그인·네트워크 실패·빈 상태·푸시 미수신)가 데모가 아니라 실제 원우 손에서 견고한가
  • 명함 QR - 상대가 앱 미설치일 때 모바일 브라우저 웹 폴백으로 명함이 반드시 전달되는가
  • iOS·Android 단일 Flutter 코드베이스가 두 플랫폼에서 동일하게 동작하는가(딥링크·푸시 포함)
  • 선제 발굴한 보안 이슈(평문 비밀번호·민감정보 로그·인증키 노출)가 실제로 닫혔는지 검증했는가
  • 핵심 흐름(로그인·푸시·원우 검색)에 회귀 테스트가 없는 상태로 인수 - "같은 입력에 같은 결과"를 어디서 보장하는가
  • stage와 운영이 정말 분리돼 있는가 - 8월 20일 운영 API가 약 20분간 stage DB를 바라본 사고(.env 소싱 뒤 pm2 --update-env). 서브셸 소싱, --update-env 금지, 배포 후 DB 확인 curl을 절차에 넣었다
  • 장애를 배포 회귀로 오인하지 않는가 - 9월 16일 AWS 미납 정지로 Route 53·EC2·RDS가 함께 멈췄는데 처음엔 배포 문제로 읽었다. 데이터 손실은 없었고, 결제 알림 부재가 남은 숙제다
  • 강제 업데이트 임계값과 키오스크 유예 종료가 어긋나 있다 - 운영 /version/check는 아직 구버전을 통과시키는데 키오스크는 토큰 없는 QR을 거절한다. 구앱 사용자가 배식에서 막히는 경로를 알고 있고 아직 닫지 못했다
  • 비로그인 명함 조회 ID가 순차적이라 열거 가능하다 - mem_privacy는 동의 플래그지 공개 플래그가 아니다. 미해결로 적어둔다
  • 회원 비밀번호 1,979건 중 1,940건이 평문이던 상태(8월 18일) - 로그인 시 bcrypt로 지연 업그레이드하는 경로를 넣었다
  • 회귀 테스트가 실제로 늘었는가 - 원우수첩 모델 테스트 24건을 옛 브랜치에서 되살렸고(PR #67), 백엔드 spec 34개·명함 웹 61개·선거 159개. 어드민은 0이고 CI/CD는 없다

Outcome

Metrics

성균관대 EMBA 원우 대상 실서비스. 2026-06-25 iOS·Android 양 스토어 출시, main 기준 2.0.0+39(2026-08-27). 앱에 표시되는 원우 1,975명(8월 24일). 6월 26일 이후 5개 레포 PR 225건(앱 59·백엔드 64·어드민 28·명함 웹 72·선거 2). 도시락 평일 명부 화 92·수 82·목 65·토 118명. 학교 공지 자동 등록 16건, 차단 0건. 120기 기수장 선거 투표율 94.6%(37명 중 35명). 다운로드 수·스토어 리뷰·배식 체크인 누계는 기록 없음.

Result / Learning

내가 짜지 않은 코드를 인수해 다루는 일의 무게를 확인. 새 기능을 얹는 건 쉽지만, 인수 코드 위에서는 "이걸 고치면 어디가 같이 깨지는지"가 안 보이는 게 진짜 비용이었다. 그래서 기능 요청을 받기 전에 핵심 흐름(로그인·푸시·원우 검색)의 경계부터 파악하는 게 먼저라는 결론. 다음 라운드는 그 경계에 회귀 테스트를 까는 것부터. 2차 라운드의 결과는 둘로 나뉜다. 제품은 넓어졌다. 명함·복지·예약·키오스크·공지 동기화·학위계산기·선거가 석 달 안에 들어갔고 외주사에 있던 스토어 계정도 넘어왔다. 반면 회귀 테스트를 깔겠다던 약속은 절반만 지켰다. 모델 테스트를 되살리고 백엔드·명함 웹·선거에 테스트를 붙였지만 CI는 아직 없고 어드민은 0건이다. 그 사이 운영 사고가 두 번 있었다. stage DB를 바라본 20분과 AWS 정지 하루. 둘 다 데이터는 잃지 않았고, 둘 다 절차로 남겼다.

Retrospective
  • 인수 직후 기능 요청부터 받기 시작했는데, 코드의 경계를 먼저 지도화한 뒤 받았어야 했다. 모르는 코드 위에 기능을 얹으면 회귀가 어디서 날지 예측이 안 된다.
  • 자동화 테스트가 거의 없는 상태로 인수했다. 핵심 경로부터 회귀 테스트를 깔아두는 걸 더 일찍 했어야 한다.
  • 한 사람 팀이 되자 "승인이 필요 없다"가 "검증이 없다"로 미끄러지기 쉬웠다. stage 검증 뒤 운영 배포를 별도 결정으로 두는 규칙은 그래서 생겼고, 8월 20일 사고는 그 규칙이 환경 변수 한 줄에도 적용된다는 걸 가르쳤다.
  • 터미널에 비밀키가 두 번 찍혔다(8월 15일, 9월 16일). 두 번 다 교체했지만, 자격증명은 화면에 치지 않고 파일로 옮기는 규칙은 두 번째 뒤에야 적었다.

PM 역할 (실질)

공식 역할은 프론트엔드였지만, 인수·일정·품질·배포·이해관계자 보고까지 비어 있던 PM 기능을 실질적으로 맡은 기록.

팀에서 맡은 공식 역할은 프론트엔드 개발(Flutter)이었다. 다만 태스크 분배, 일정·리스크 관리, 진행 가시화, 이해관계자 보고 같은 PM 기능이 비어 있어, 개발을 직접 하면서 그 공백을 실질적으로 메웠다. 이전 외주사가 만들어 둔 앱을 인수받아 분석하고, 흩어져 있던 요구사항·일정·권한·품질·배포를 하나로 묶어 약 두 달 만에 총학생회 임기 종료 직전 iOS·Android 동시 출시까지 끌고 갔다. 아래는 그 PM 역할의 기록이다.

프로젝트 인수 & 권한 이관

남이 짜 둔 코드와 흩어진 운영 권한을 통제 가능한 상태로 만드는, 시작점의 가장 번거롭고 중요한 일을 맡았다.

  • 직접 작성하지 않은 기존 코드를 전수 분석하고 수정·리팩토링 항목을 리스트업 (인수 코드라 더 오래 걸리는 핸디캡을 감수)
  • 앱·어드민·백엔드 GitHub 저장소 3종을 신규 구축하고 이관
  • AWS 접근 정리 - RDS·IP 화이트리스트 이슈를 식별하고 해결 요청
  • Google Play Console 관리자 권한 확보, 외주사 계정 권한 회수 확인
  • Apple Developer 팀 관리자 초대, 인증키(.p8) revoke·재발급, 외주사 멤버 제거, 배포 인증서 점검
  • 막히는 지점에서는 백엔드까지 같이 맡겠다고 자원해 범위 공백을 메움

역량 · 일정 · 우선순위 · 리스크 · 자원 조정

스코핑 & 요구사항 합의

  • 흩어져 있던 논의를 회의록과 합의된 범위로 고정
  • 인수 시점에 "스펙 100% 완료"를 받는 것을 원칙으로 삼아, 핵심 스펙 차이를 발견하고 재합의
  • 결정이 지연되던 식단 기능을 보류하고 검색·필터·웹진·명함 UI 3종을 먼저 완료해 지연 전파를 차단

역량 · 스코핑 · 범위 합의

일정 & 병목 관리

  • 마감에서 역산한 일정을 세우고 진행
  • 백엔드 병목을 태스크 단위로 분할해 전달하고 진행 상황을 투명하게 보고
  • 이해관계자의 일정 질문에 항목별로 분리해 답하고, 모호한 질문은 답변 가능한 형태로 재정의한 뒤 답변

역량 · 일정 · 병목 관리

진행 가시화 & 이해관계자 커뮤니케이션

개발팀과 비개발자(학생회) 사이의 번역기이자 단일 보고 창구 역할을 했다.

  • 상태 보드 운영 - 완료/진행중/진행전, Frontend·Backend 분리, 기존 요구사항과 추가 요구사항 구분
  • 데일리 진행 리포트를 학생회 전원에게 정기 공유, 비개발자가 이해할 수 있는 언어로 풀어서
  • 리스크·제약을 숨기지 않고 공유하면서 현실적 대안 세트를 함께 제시 (외주 재의뢰 / 범위 조정 / 우선순위 조정)
  • 압박 상황에서 책임을 먼저 수용하고 재발 방지로 전환

역량 · 스테이크홀더 관리 · 리포팅 · 기대 관리

품질 & 보안

14년 QA 경험을 요청 범위 밖에서도 적용했다.

  • 요청 범위 외 보안 이슈 6건 이상을 선제 발굴·수정 (평문 비밀번호, 민감정보 로그, 인증키 노출 등)
  • 비개발자용 테스트 환경 구축 - Firebase App Distribution, 테스터 등록, 데모 영상
  • QA 리포트와 테스트 가이드를 직접 제작

역량 · 품질 · 보안 · 테스트 환경

배포 & 릴리스

iOS·Android 동시 출시를 임기 종료 직전에 완주했다.

  • 명함용 전용 도메인과 딥링크·앱 자동 연결을 설정·배포 - 앱 미설치 외부인도 명함을 열람하도록
  • "금·주말 배포 지양" 원칙을 알면서도, 모니터링을 조건으로 임기 내 노출을 위해 금요일 배포를 결정
  • 결과: 2026-06-25 Android·iOS 양 스토어 심사 정상 승인, 당일 배포

역량 · 릴리스 · 앱스토어 운영 · 인프라 의사결정

운영 이관 & 지속가능성

프로젝트를 끝내는 게 아니라 다음 주체가 이어받을 수 있게 마무리했다.

  • 식단 기능 - 데이터 부재·운영 주체 미정 이슈를 정리하고, 프론트 영역 선반영 + 백엔드는 방학 중 처리하는 단계적 안 제시
  • 차기(66대) 학생회·회장과의 인수인계 논의를 직접 책임으로 가져감
  • 동아리 참여비 입금 현황 자동 체크 등 운영 효율화 아이디어 발굴
  • 연락처 자동 저장, 명함 레이아웃, 무직 회원 표기 일관성 등 사용성 개선 다수 처리

역량 · 운영 전환 · 핸드오버 · 제품 개선

2차 라운드 - 스토어 계정 이관과 운영 기능

출시 뒤 외주사 개발자가 빠지면서 앱·백엔드·어드민·명함 웹을 한 사람이 맡게 됐다. 기능보다 먼저 한 일은 계정과 환경을 통제 가능하게 만드는 것이었다.

  • 외주사 보유 Apple·Google Play 계정의 App Transfer를 9월 4일 시작해 17일 마무리, 개인정보처리방침·지원 페이지 신설
  • stage/운영 DB 분리(8월 15일)와 "운영 배포는 별도 결정" 규칙, 배포 후 DB 확인 절차
  • 총학생회 운영에 필요한 기능을 순서대로 - 도시락 키오스크, 지원 이용처 예약, 학교 공지 자동 동기화, 기수장 선거
  • 앱 배포 1회 비용(외주 경유 20만원)을 알고, 앱 배포 없이 고칠 수 있게 웹 쪽 핸들러를 미리 깔아두는 식으로 배포 빈도를 설계
  • AWS 키 노출 2회와 미납 정지 1회를 사고 기록으로 남기고 재발 방지 규칙을 적음

역량 · 계정·환경 통제 · 운영 기능 · 사고 기록

백엔드 개발자 협업 역량 (자기평가)

FE 출신이 아니어서 협업을 잘할 수 있을지 의심이 있었다. 백엔드 협업 채널 로그를 근거로 한 자기평가 - 강점과 다듬을 점.

프론트엔드 개발자 출신이 아니어서, 백엔드와 협업을 잘 해낼 수 있을지 스스로 의심이 있었다. 그래서 백엔드 협업 채널 로그(약 2주)를 근거로 협업 방식을 돌아봤다. 돌아보니 협업을 굴러가게 한 건 "FE 코딩 실력"이 아니라 인터페이스로 사고하는 힘, 요구사항 규율, 이슈 트래킹, 품질 게이트, 우선순위 정리, 그리고 사람을 대하는 태도였다. 한 분야만 깊게 판 개발자가 오히려 자주 약한 부분이다. 잘한 점과 다듬을 점을 같이 적었다.

  • 인터페이스·의존관계 사고★★★★★
  • 협업 도구·프로토콜 활용★★★★★
  • 기술적 근거로 모호함 해소★★★★☆
  • 품질·릴리스 게이트 관리★★★★★
  • 우선순위·집중 관리★★★★☆
  • 대인 톤·심리적 안전감★★★★☆
  • 텍스트 커뮤니케이션 톤 · 텍스트에선 톤을 한 번 더 다듬을 여지★★★☆☆

강점

인터페이스로 사고한다

FE·BE 경계를 계약(인터페이스)으로 보고, 무엇을 주고받을지와 의존 관계를 먼저 정리한 뒤 작업을 시작했다.

협업 도구·프로토콜을 능숙하게 쓴다

필요한 API를 엔드포인트와 필드 목록 단위로 명세하고, 서브도메인 이슈는 직접 작은 express 서버를 만들어 레포에 올려 선제적으로 언블록했다. 요청에는 스크린샷 근거를 붙였다.

모호함을 근거로 닫는다

"특정 기수가 검색 드롭다운에 안 뜬다"는 모호한 이슈를 가설 수립 → 직접 테스트 → 실제 코드 확인(기수 필드가 nullable이라 "선택 안 함" 상태로 등록됨)까지 파서 "추가 수정 불필요, 현 상태 유지"로 확정했다. 동시에 지금 할 일과 나중에 개선할 일(필수값화·CSV 일괄등록 등)을 분리했다.

품질·배포 게이트를 지킨다

백엔드가 실서버 배포를 예고했을 때, 배포 전 QA·통합 확인 게이트를 요구해 검증 없는 배포를 막았다. 협업에서 가장 인상적이었던 지점.

심리적 안전감을 만든다

본인 실수를 먼저 인정하고("제가 누락한 것 같습니다"), 수정 부담을 상대에게 떠넘기지 않았다("구조적으로 문제없으니 제가 하는 게 낫겠다"). 그 결과 상대도 방어적이지 않게 자기 실수를 편히 말할 수 있었다.

다듬을 점

빠른 확인 욕구를 텍스트 톤으로 한 번 더 다듬기

이슈가 보이면 빠르게 확인하고 짚으려는 성향이 강하다. 대면에선 추진력이 되지만, 텍스트에선 같은 의도가 더 건조하게 비칠 수 있어 한 번 더 톤을 챙기면 협업이 한결 매끄러워진다.

개선 "이 부분은 제가 맥락을 놓친 것 같은데, 한 번만 정리해 주실 수 있을까요?"처럼 한 줄을 더하면 같은 정보를 더 부드럽게 주고받을 수 있다.

Tech stack
  • Flutter
  • Dart
  • Firebase Cloud Messaging
  • REST API (dio)
  • Hive
  • get_it
  • easy_localization
  • WebView
  • iOS
  • Android
  • NestJS · TypeORM · MySQL (백엔드)
  • Next.js (어드민)
  • Express (명함 웹 · 키오스크 · 선거)
  • AWS EC2 · RDS · ALB