EMBA E-Members
A community app for SKKU EMBA members - the member directory, business-card QR sharing, mentoring, schedule, board, and cafeteria menu in one place. After the June launch on both stores, a 2.0 round widened it to business cards, welfare, bookings, a meal kiosk, the degree planner, and elections, and that round, backend and admin included, was effectively a one-person job.
- Flutter
- NestJS
- Firebase
- iOS
- Android
- AWS
My page - manage academic, club, and company info in one place, with QR business card and profile editing
Business card sharing - scanning my QR opens the EMBA app, or falls back to a web business card if the app isn't installed
Cafeteria menu - browse the school cafeteria menu by week, with photos
Setup
- Problem
An EMBA peer network is itself an asset, but that asset ends up scattered across KakaoTalk rooms, spreadsheet rosters, paper business cards, and school notice emails. "Which cohort was that person again?", "where's that notice?", "what's for lunch today?" each become a hunt through separate channels. What members need isn't flashy features but the basics - "find people, exchange cards, and don't miss the schedule or news, all in one place."
- Context
The official member community app for SKKU EMBA. The first round (handover on 30 April to launch on 25 June) was a small team of 1 PM and 2 developers, with James as PM and one of the developers: defining scope and priorities, developing and maintaining the inherited Flutter codebase, and carrying it to a simultaneous iOS and Android launch. The second round (26 June to October) was different. The vendor's backend developers made their last commit on 14 June, so every commit and all 225 PRs across five repos since then - app, backend (NestJS and MySQL), admin (
Next.js), card web (Express), and the election system - were one person's work. The app version on main is 2.0.0+39 (2026-08-27). Whether 2.0.0 actually reached the stores is not recorded in the repos, and a note from 18 September says production was still not forcing old versions to update. Only what is verified is written here. The basics a single member relies on end to end - finding people, exchanging cards, schedule and notices - came first, then it widened into what the student council actually needed to run: meal distribution, bookings, and elections.- Users
Current and graduated SKKU EMBA members, plus cohort organizers.
- Hypothesis
Let members handle people, cards, schedule, and news in one app instead of scattered channels, and the EMBA network becomes an asset that actually reaches you when you need it.
Build
- What I did
- As PM, defined feature scope and priorities and ran releases/updates on both the iOS and Android stores
- Took over the existing Flutter codebase and develop and maintain it (2.0.0+39 on main as of 2026-08-27, after the 25 June launch)
- Member directory - search and filter by cohort, job field, and name, and save a member's profile straight into the phone's contacts
- Business-card QR sharing - scanning opens the EMBA app, with a fallback to a web business card if the app isn't installed
- Mentoring - find and request a mentor, with 1:1 chat
- Schedule calendar (academic, cohort, personal), board and notices (with a built-in PDF viewer for attachments), school news and webzine (WebView), and a weekly cafeteria menu
- FCM push notifications and KO/EN localization
- The 2.0 round (July to September) - two business cards (the school card drawn by the app, the company card as an uploaded image), a 3-minute token and screenshot blocking on the my-page QR, welfare and partner benefits plus transit guidance tabs, bookings at council-subsidised venues, automatic school notice sync (attachments copied to our own server), a live shuttle modal
- Meal distribution kiosk - QR token validation on a tablet kiosk (/kiosk) and a staff phone (/operator), per-session and per-weekday rosters with reserves, remaining stock shown in the app, weekday auto-open at 16:00 and close at 23:00 (Saturday 10:00 and 18:00), and holiday gift pickup in a separate table
- The backend (NestJS, TypeORM, MySQL), admin (
Next.js), and card web (Express) too - admin levels 300/255/233/222 with a scope decorator, lazy bcrypt upgrade on login, academic calendar and admission guide PDF management, real paging on the board - Embedded the degree planner as a web view - remote bundle first, bundled app asset as fallback. Its data moved from Supabase to the EMBA backend
- Built a central election system for the cohort 120 leader election in a day and mounted it on the card web (17 September), then ran three rounds of a slogan vote on the same structure
- Started the App Transfer of the Apple and Google Play accounts held by the vendor on 4 September and closed it on the 17th - new privacy policy and support pages on the card web
- The 66th council rebrand (2.0.0) - colours, slogan, and board ID pulled into a CouncilBrand layer so the next council can change them again
- Product decisions
- Basics first - finding people, cards, schedule, and notices made solid before any flashy social features
- Cards as QR plus a web fallback - so a card still gets delivered even if the other person hasn't installed the app; no forcing an install in a networking moment
- Save contacts into the phone's native address book - an asset that stays even after leaving the app
- A single iOS/Android codebase (Flutter) - so a small team can maintain both platforms at once
- QR as a 3-minute token with screenshot blocking - the card QR doubles as the meal check-in, so a reused screenshot has to fail. The kiosk validates the token once the grace period ended
- Proxy pickup was built and reverted the same day (20 August) - once it was clear it broke one-person-one-meal uniqueness, holiday gifts went into a separate table so they never mix with meal uniqueness
- School notice attachments are copied to our own server - the app cannot open external links, so that constraint was absorbed into the sync design. The welfare source watcher cron only compares hashes; writes need a human
- Email is left out of the unauthenticated card API - a card must be delivered, but it must not become a harvestable contact
- A flowing "SKKU EMBA" mark only on the partner detail screen - the purpose is forgery prevention, so its scope was narrowed twice
- QA considerations
- The boundaries of inherited code (token expiry/re-login, network failures, empty states, missed push) hold up in real members' hands, not just a demo
- Business-card QR - when the other person hasn't installed the app, the mobile-browser web fallback still delivers the card
- The single Flutter codebase behaves identically on iOS and Android (including deep links and push)
- The proactively-found security issues (plaintext passwords, sensitive data in logs, exposed keys) are verified as actually closed
- Inherited with no regression tests on the core flows (login, push, member search) - where is "same input, same result" guaranteed?
- Are stage and production really separated - on 20 August the production API pointed at the stage DB for about 20 minutes (sourcing .env then pm2 --update-env). Subshell sourcing, no --update-env, and a post-deploy DB sanity curl went into the procedure
- Is an outage being misread as a deploy regression - on 16 September an AWS non-payment suspension stopped Route 53, EC2, and RDS together, and it was first read as a deploy problem. No data was lost; the missing billing alert is the open item
- The force-update threshold and the kiosk grace period are out of step - production /version/check still lets old versions through while the kiosk refuses a QR without a token. The path where an old-app user is turned away at the meal line is known and not yet closed
- Unauthenticated card lookups use sequential IDs, so they can be enumerated -
mem_privacyis a consent flag, not a publish flag. Recorded as unresolved - 1,940 of 1,979 member passwords were plaintext (18 August) - a lazy bcrypt upgrade on login was added
- Did regression tests actually grow - 24 member-directory model tests were restored from an old branch (PR #67); backend 34 spec files, card web 61 tests, election 159. Admin has 0 and there is no CI/CD
Outcome
- Metrics
A live service for SKKU EMBA members. Launched on both the iOS and Android stores on 2026-06-25, 2.0.0+39 on main (2026-08-27). 1,975 members shown in the app (24 August). 225 PRs across five repos since 26 June (app 59, backend 64, admin 28, card web 72, election 2). Weekday meal rosters of 92 (Tue), 82 (Wed), 65 (Thu), and 118 (Sat). 16 school notices auto-registered, 0 blocked. Cohort 120 leader election turnout 94.6% (35 of 37). Download counts, store reviews, and cumulative meal check-ins are not recorded.
- Result / Learning
Confirmed the weight of taking over and running code I didn't write. Adding a new feature is easy; the real cost on inherited code is that "if I change this, what else breaks?" isn't visible. So the conclusion was that mapping the boundaries of the core flows (login, push, member search) comes before taking feature requests. The next round starts by laying regression tests on those boundaries. The second round's result splits in two. The product widened: cards, welfare, bookings, the kiosk, notice sync, the degree planner, and elections landed within three months, and the store accounts came over from the vendor. The promise to lay regression tests was only half kept: model tests were restored and tests were added to the backend, card web, and election, but there is still no CI and admin has none. In between there were two incidents, 20 minutes pointed at the stage DB and a day of AWS suspension. Neither lost data, and both became written procedure.
- Retrospective
- Right after the handover I started taking feature requests; I should have mapped the code's boundaries first. Stacking features on code you don't know makes regressions impossible to predict.
- It was inherited with almost no automated tests. I should have laid regression tests on the core paths earlier.
- Once it became a one-person team, "no approval needed" slid easily toward "no verification". The rule that a production deploy is a separate decision after stage verification exists because of that, and the 20 August incident taught that the rule applies to a single environment variable line too.
- Secret keys were echoed to the terminal twice (15 August, 16 September). Both were rotated, but the rule of never typing credentials on screen and moving them as files was written only after the second time.
PM role (de facto)
Formally a frontend dev, but I filled the vacant PM function - handover, schedule, quality, release, and stakeholder reporting.
PM role (de facto)
My formal role on the team was frontend development (Flutter). But the PM functions - task allocation, schedule and risk management, progress visibility, stakeholder reporting - were vacant, so I filled that gap in practice while developing. I took over the app the previous vendor had built, analyzed it, and pulled the scattered requirements, schedule, access, quality, and release into one thread - shipping to both the iOS and Android stores at the same time, just before the student council's term ended, in about two months. Below is the record of that PM role.
Project handover & access transfer
I took on the messiest, most important work at the start: turning someone else's code and scattered ops access into a state I could actually control.
- Audited the entire existing codebase I hadn't written and listed the fixes/refactors (accepting the handicap that inherited code takes longer)
- Stood up and migrated three new GitHub repos - app, admin, backend
- Cleaned up AWS access - identified and escalated RDS / IP-whitelist issues
- Secured Google Play Console admin rights and confirmed the vendor's access was revoked
- Invited an Apple Developer team admin, revoked and reissued the signing key (.p8), removed the vendor's member, and checked the distribution certificates
- Volunteered to take on the backend too where things stalled, to cover the scope gap
Skills · Schedule · Prioritization · Risk · Resourcing
Scoping & requirements alignment
- Fixed scattered discussions into meeting notes and an agreed scope
- Made "hand it over spec-complete" the principle for the handover, which surfaced key spec gaps to re-align on
- Parked the cafeteria-menu feature while its decision lagged, and finished the search/filter, webzine, and business-card UI first to stop the delay from spreading
Skills · Scoping · Scope alignment
Schedule & bottleneck management
- Built and ran a schedule worked backward from the deadline
- Split the backend bottleneck into discrete tasks and reported progress transparently
- Answered stakeholders' schedule questions item by item, and reframed vague questions into answerable ones before answering
Skills · Schedule · Bottleneck management
Visibility & stakeholder communication
I was the translator and single reporting channel between the dev team and the non-technical student council.
- Ran a status board - done / in progress / not started, frontend and backend split out, existing vs. added requirements distinguished
- Shared a daily progress report with the whole council, written in language a non-developer could follow
- Surfaced risks and constraints rather than hiding them, always paired with a set of realistic options (re-outsource / adjust scope / re-prioritize)
- Under pressure, took responsibility first and pivoted to prevention
Skills · Stakeholder management · Reporting · Expectation setting
Quality & security
I applied fourteen years of QA even outside the requested scope.
- Proactively found and fixed 6+ security issues outside the request (plaintext passwords, sensitive data in logs, exposed keys, etc.)
- Built a test environment for non-developers - Firebase App Distribution, tester registration, demo videos
- Wrote the QA reports and test guides myself
Skills · Quality · Security · Test environment
Release
I carried the simultaneous iOS/Android launch across the line, just before the term ended.
- Set up and deployed a dedicated business-card domain with deep links and app auto-open - so even non-members without the app could view a card
- Knowing the "avoid Friday/weekend releases" rule, still decided on a Friday release - conditioned on monitoring - to ship within the term
- Result: on 2026-06-25 both the Android and iOS reviews were approved and shipped the same day
Skills · Release · App-store ops · Infra decisions
Operations handover & sustainability
I closed this out not by "ending" it but by making it something the next owners could pick up.
- Cafeteria menu - documented the missing-data and undecided-owner issues, and proposed a phased plan (frontend done now, backend over the break)
- Took direct ownership of the handover discussion with the next (66th) student council and its president
- Surfaced ops-efficiency ideas like auto-checking club-fee payment status
- Handled many usability improvements - auto-saving contacts, card layout, consistent labeling for members with no employer
Skills · Operations transition · Handover · Product improvement
Second round - store account transfer and operations features
After launch the vendor's developers left, and the app, backend, admin, and card web fell to one person. Before features, the first job was getting accounts and environments under control.
- Started the App Transfer of the vendor-held Apple and Google Play accounts on 4 September and closed it on the 17th, with new privacy policy and support pages
- Separated the stage and production DBs (15 August), made "production deploy is a separate decision" the rule, and added a post-deploy DB check
- Built what the student council needed to operate, in order - the meal kiosk, subsidised venue bookings, automatic school notice sync, the cohort leader election
- Knowing an app release cost money each time (via the vendor), designed release frequency around it - pre-wiring web-side handlers so fixes could ship without an app release
- Recorded two AWS key exposures and one non-payment suspension as incidents and wrote the prevention rules
Skills · Account and environment control · Operations features · Incident records
Collaboration with the backend dev (self-assessment)
I'm not a frontend dev by background and doubted I could collaborate well - a self-assessment grounded in the backend collaboration channel logs: strengths and growth areas.
Collaboration with the backend dev (self-assessment)
I'm not a frontend developer by background, so I doubted whether I could collaborate well with the backend. So I reviewed how I actually worked, grounded in the backend collaboration channel logs (about two weeks). Looking back, what kept the collaboration running wasn't "frontend coding skill" but thinking in interfaces, requirements discipline, issue tracking, quality gates, prioritization, and how I treated people - the very things developers who've only gone deep in one area are often weak at. I wrote down both what went well and what to improve.
- Interface / dependency thinking★★★★★
- Collaboration tools & protocol★★★★★
- Closing ambiguity with evidence★★★★☆
- Quality / release gatekeeping★★★★★
- Prioritization & focus★★★★☆
- Tone & psychological safety★★★★☆
- Text communication tone · in text, room to soften the tone★★★☆☆
Strengths
Thinks in interfaces
I treated the FE/BE boundary as a contract - settling what gets exchanged and the dependencies first, before starting the work.
Fluent with collaboration tools and protocol
I specified the APIs I needed down to endpoints and field lists, and when a subdomain issue blocked us I built a small express server myself and pushed it to the repo to unblock proactively. I attached screenshots as evidence.
Closes ambiguity with evidence
For a vague issue - "a certain cohort doesn't appear in the search dropdown" - I went hypothesis -> hands-on test -> reading the actual code (the cohort field is nullable, so it had been registered as "not selected"), and concluded "no fix needed, leave as is." I also split what to do now from what to improve later (make it required, CSV bulk import, etc.).
Holds the quality and release gate
When the backend announced a production deploy, I asked for a QA / integration check gate before shipping, stopping a deploy with no verification. The most striking part of the collaboration.
Creates psychological safety
I admitted my own mistakes first ("I think I missed that") and didn't push the fix burden onto the other person ("it's structurally fine, better that I handle it"). As a result, they too could name their own mistakes without getting defensive.
Growth areas
Polishing the text tone behind a fast urge to confirm
When I spot an issue, I have a strong urge to confirm and pin it down quickly. In person that reads as drive, but in text the same intent can come across drier, so taking one more pass on tone makes collaboration smoother.
Try Adding one line - "I think I lost the context here; could you walk me through it once?" - gets the same information across more gently.
- 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