콘텐츠 대표 이미지 - Swift 개발자의 포트폴리오, 서류 통과부터 기술면접까지 뚫는 구성 전략
🍎 Swift · 프로그램개발

Swift 개발자의 포트폴리오, 서류 통과부터 기술면접까지 뚫는 구성 전략

"앱 3개 만들었는데 왜 다 떨어지죠?" 라는 질문에 대한, 조금 아픈 대답을 해볼게.

안녕 👋 오늘은 iOS 취준생이랑 주니어 Swift 개발자들이 제일 많이 묻는 그거,
"포트폴리오 어떻게 만들어야 붙어요?"를 아주 현실적으로 파헤쳐볼 거야.

미리 스포하자면 핵심은 이거야.
포트폴리오는 "앱 자랑"이 아니라 "의사결정 기록"이다.

면접관이 궁금한 건 네가 만든 화면이 예쁜지가 아니야.
"이 사람을 우리 팀 코드베이스에 넣었을 때 사고 안 치고 잘 굴러갈까?"
딱 이거 하나야. 자, 그럼 하나씩 뜯어보자.

면접관이 포트폴리오를 보는 순서 평균 열람 시간 3~7분 · 대부분 README에서 판단이 끝난다 1. README 30초 · 스크린샷 2. 아키텍처 폴더 구조 · 계층 3. 커밋 로그 습관 · 협업 흔적 4. 실제 코드 2~3개 파일 탈락 사유 분포 (주니어 iOS 기준 체감치) README 부실 / 실행 불가 튜토리얼 복제 티가 남 테스트·에러처리 전무 설명 못 함(면접에서 무너짐) 1위 2위 3위 4위

1. 개수의 함정 — 앱 5개 < 잘 만든 앱 2개

제일 먼저 깨야 할 미신부터.
"프로젝트 많으면 유리하겠지?" → 아니야. 오히려 마이너스인 경우가 많아.

왜냐면 프로젝트가 5개인데 전부 '로그인 + 리스트 + 상세화면'이면,
면접관 입장에선 같은 난이도 문제를 5번 푼 것으로 보여.
성장 곡선이 안 보이는 거지.

이상적인 구성비

🥇 메인 프로젝트 1개 — 깊이 담당. 아키텍처, 테스트, 성능 개선, 배포까지 전 과정.

🥈 서브 프로젝트 1~2개 — 넓이 담당. 메인에서 못 보여준 기술(예: 동시성, 위젯, 결제) 커버.

🥉 작은 실험/라이브러리 0~2개 — 호기심 담당. SPM 패키지, UIKit 커스텀 컴포넌트 등.

여기서 메인 프로젝트는 반드시 "왜 이걸 만들었는가"에 답이 있어야 해.
"내가 매일 겪는 불편함을 해결하려고" 같은 개인적 동기면 충분해.
오히려 그런 동기가 있는 앱이 기능 설계도 더 뾰족하거든.

튜토리얼 따라 만든 날씨앱 3개보다,
"헬스장 기구 대기열 공유 앱"처럼 못생겼어도 문제의식이 명확한 앱 1개가 훨씬 세다.

2. README가 곧 포트폴리오다 (진심으로)

냉정하게 말하면, 채용 담당자가 네 Xcode 프로젝트를 클론해서 빌드해볼 확률은 낮아.
거의 모든 판단이 GitHub README 스크롤 한 번에서 끝난다.

README에 반드시 들어가야 할 8가지

항목내용왜 필요한가
한 줄 소개"○○를 위한 △△ 앱"3초 안에 맥락 전달
스크린샷/GIF핵심 플로우 3~5컷실행 안 해도 보인다
기술 스택Swift 버전, 최소 iOS 타깃, 라이브러리환경 호환성 판단
아키텍처 다이어그램계층 흐름도 1장설계 사고력 증명
기술적 의사결정"A 대신 B를 쓴 이유"여기가 진짜 핵심
트러블슈팅문제 → 원인 → 해결 → 결과디버깅 역량
성능 지표개선 전/후 수치정량적 설득력
실행 방법클론 후 명령어협업 배려

💡 트러블슈팅 작성 템플릿 (이대로만 쓰면 반은 먹고 들어감)

상황: 리스트 스크롤 시 프레임이 40fps 아래로 떨어짐
원인: 셀마다 이미지 다운로드/리사이징을 메인 스레드에서 동기 수행
해결: 다운샘플링 + NSCache 기반 이미지 캐시 도입, 디코딩을 백그라운드로 이동
결과: Instruments Time Profiler 기준 메인 스레드 점유 62% → 18%, 스크롤 58~60fps 유지
남은 과제: 캐시 용량 정책이 단순 개수 제한이라 메모리 압박 시 대응 필요

봐봐, 마지막에 "남은 과제"까지 쓴 거 포인트야.
자기 코드의 한계를 아는 사람 = 성장할 사람. 면접관이 제일 좋아하는 시그널이거든.

3. UIKit이냐 SwiftUI냐, 그 영원한 떡밥

2025년 기준 현실적인 답은 "둘 다, 단 비중을 나눠서"야.

SwiftUI — 신규 프로젝트, 스타트업, Apple의 기본 방향. 위젯·watchOS는 사실상 필수.
UIKit — 국내 중대형 서비스의 레거시 코드베이스 대부분. 복잡한 커스텀 UI, 세밀한 스크롤 제어.

그래서 추천 조합은 이래.

👉 메인 프로젝트는 SwiftUI로 (트렌드 + 생산성 어필)
👉 서브 프로젝트 하나는 UIKit 코드베이스 오토레이아웃으로 (기본기 어필)

특히 UIKit 프로젝트는 스토리보드 말고 코드 베이스를 권장해.
스토리보드는 협업 시 머지 충돌 지옥이라 실무에서 기피하는 곳이 많고,
코드로 짜면 Auto Layout 제약 이해도가 그대로 드러나거든.

SwiftUI 쓴다면 이건 알고 써야 해

// ❌ 흔한 실수: 뷰가 그려질 때마다 객체가 새로 생성됨
struct FeedView: View {
    @StateObject private var vm = FeedViewModel()   // ✅ 소유는 StateObject
    // @ObservedObject private var vm = FeedViewModel()  // ❌ 재생성 위험
    var body: some View { ... }
}

// ✅ iOS 17+ 라면 Observation 매크로
@Observable
final class FeedViewModel {
    private(set) var items: [Feed] = []
    func load() async { ... }
}

면접에서 "@State / @StateObject / @ObservedObject / @Binding 차이 설명해보세요"는
거의 고정 질문이야. 네 프로젝트 코드로 설명할 수 있어야 해.

4. 아키텍처: MVVM만 쓰면 되냐고? 반은 맞아

주니어 포트폴리오의 90%가 MVVM이야. 그건 문제가 아니야.
문제는 "MVVM 썼습니다"라고만 써놓고 실제론 ViewModel이 1500줄짜리 신(God) 객체인 경우.

면접관의 진짜 질문은 이거야

"MVVM을 왜 선택했어요?" → MVC로 하면 ViewController가 비대해져서요 (👌 무난)

"그럼 ViewModel은 어떻게 테스트해요?" → ... (💀 여기서 무너짐)

그래서 아키텍처를 말할 땐 항상 "테스트 가능성"과 세트로 가야 해.
네트워크 레이어를 프로토콜로 추상화해두면 자연스럽게 답이 나와.

protocol FeedRepository {
    func fetchFeeds(page: Int) async throws -> [Feed]
}

final class DefaultFeedRepository: FeedRepository { /* URLSession */ }
final class MockFeedRepository: FeedRepository { /* 고정 데이터 */ }

// ViewModel은 구체 타입을 모른다 → 테스트에서 Mock 주입 가능
final class FeedViewModel {
    private let repository: FeedRepository
    init(repository: FeedRepository) { self.repository = repository }
}

이렇게만 해놔도 README에 이렇게 쓸 수 있어.

"Repository를 프로토콜로 추상화하여 ViewModel이 네트워크 구현에 의존하지 않도록 했고,
덕분에 MockRepository를 주입한 12개의 유닛 테스트를 서버 없이 실행할 수 있습니다."

이 한 문장이 "MVVM 적용함" 백 번보다 강해.

의존성 방향이 한쪽으로 흐르는 구조 화살표가 역주행하지 않는지만 확인해도 설계 절반은 성공 Presentation View · ViewModel · 상태 관리 SwiftUI / UIKit Domain Entity · UseCase · Repository 프로토콜 순수 Swift Data URLSession · SwiftData · Keychain 구현체 Domain은 아무에게도 의존하지 않는다 = 테스트가 가장 쉬운 층

5. 동시성(Concurrency)은 이제 선택이 아니다

Swift 6 이후 Strict Concurrency Checking이 본격화되면서,
async/await와 actor를 모르면 실무 코드 읽기 자체가 어려워졌어.

포트폴리오에 이거 하나만 녹여도 차별화돼.

// 이미지 캐시를 actor로 → 데이터 레이스 원천 차단
actor ImageCache {
    private var storage: [URL: UIImage] = [:]

    func image(for url: URL) -> UIImage? { storage[url] }
    func insert(_ image: UIImage, for url: URL) { storage[url] = image }
}

// 병렬 요청은 async let / TaskGroup
func loadProfile() async throws -> Profile {
    async let user = api.fetchUser()
    async let posts = api.fetchPosts()
    return try await Profile(user: user, posts: posts)   // 두 요청 동시 실행
}

면접 단골 질문 3종

① Task와 Task.detached의 차이는? (컨텍스트 상속 여부)
② @MainActor를 붙이면 정확히 무엇이 보장되나? (메인 스레드 격리)
③ Combine에서 async/await로 옮긴 경험이 있나? (마이그레이션 감각)

여기서 중요한 건, "썼다"가 아니라 "왜 여기에 썼는지"야.
단순 API 호출 하나에 TaskGroup 쓰면 오히려 과설계로 감점당해.
기술은 문제에 맞게 쓸 때만 실력으로 보인다.

6. 테스트 코드 — 커버리지 80%는 필요 없다

주니어 포트폴리오에서 테스트가 있는 비율은 체감상 20%도 안 돼.
그 말은 테스트 몇 개만 있어도 상위권이라는 뜻이야. 가성비 최고의 항목.

테스트, 이 정도만 해도 충분해

✅ ViewModel의 핵심 상태 전이 3~5개 (로딩 → 성공 / 로딩 → 실패)
✅ 날짜·금액 포맷터 같은 유틸 함수
✅ 네트워크 에러 매핑 로직 (401 → .unauthorized)
❌ getter/setter 테스트 같은 숫자 채우기용 테스트는 오히려 역효과

@Test("네트워크 실패 시 error 상태로 전이한다")
func fetchFailure() async {
    let sut = FeedViewModel(repository: MockFeedRepository(result: .failure(.timeout)))
    await sut.load()
    #expect(sut.state == .error(.timeout))
}

참고로 Xcode 16부터는 Swift Testing(@Test, #expect)이 정식 포함됐어.
XCTest만 알던 사람이 Swift Testing까지 쓰면 "최신 흐름 따라가는 사람"으로 보여. 작은 디테일이지만 효과 좋아.

7. 커밋과 브랜치, 네 성실함의 지문

은근히 많이 보는 게 커밋 히스토리야.
왜냐면 코드는 베껴도, 커밋 로그는 못 베끼거든.

🚫 이런 커밋✅ 이런 커밋
"수정", "ㅇㅇ", "final_final2""fix: 셀 재사용 시 이미지 깜빡임 해결"
3일에 걸친 작업을 1커밋에 몰아넣기기능 단위로 쪼갠 커밋
main 브랜치에만 직행feature 브랜치 → PR → 머지
PR 본문 텅 비어있음PR에 변경 요약 + 스크린샷

혼자 하는 프로젝트여도 PR을 열고 셀프 리뷰 코멘트를 달아두면
"협업 프로세스를 이해하고 있다"는 강력한 신호가 돼.
Conventional Commits(feat/fix/refactor/chore) 규칙 하나만 지켜도 인상이 확 달라져.

🐣 잔디밭 얘기 잠깐

매일 커밋하는 건 필수 아니야. 억지 잔디는 오히려 티 나.
중요한 건 "한 프로젝트를 몇 주~몇 달에 걸쳐 꾸준히 발전시킨 흔적"이야.
2주 만에 만들고 6개월 방치된 레포 5개보다, 4개월간 천천히 자란 레포 1개가 낫다.

8. 앱스토어 배포 — 있으면 압도적으로 유리한 이유

배포 경험이 있으면 '완성시켜본 사람' 카테고리로 분류돼.
이게 생각보다 어마어마한 차이야. 왜냐면 배포엔 코딩 말고도 넘어야 할 산이 많거든.

배포하면 자동으로 증명되는 것들

· 인증서/프로비저닝 프로파일 이해
· App Store Connect, TestFlight 운영
· 개인정보 처리방침, 앱 추적 투명성(ATT) 대응
· 리뷰 리젝 대응 경험 (이거 면접 스토리로 아주 좋아)
· 크래시 로그 확인 및 핫픽스 배포

"심사에서 Guideline 4.3(스팸)으로 리젝당했고, 기능 차별점을 보강해서 재제출로 통과했습니다"
이런 스토리 하나면 면접 10분은 그냥 채워. 진짜로.

앱 출시가 부담스러우면 TestFlight 링크라도 README에 넣자.
면접관이 직접 설치해볼 수 있는 앱은 신뢰도가 다르다.

9. 이력서에 쓰는 문장, 이렇게 바꿔봐

같은 작업도 표현에 따라 체급이 달라져. 실제 예시로 비교해볼게.

Before — "Alamofire를 사용하여 네트워크 통신을 구현했습니다."

After — "외부 의존성을 줄이기 위해 URLSession 기반 네트워크 레이어를 직접 구현했고,
Generic + async/await로 엔드포인트를 타입 안전하게 추상화해 API 추가 시 보일러플레이트를 약 60% 줄였습니다."

Before — "CoreData로 데이터를 저장했습니다."

After — "오프라인에서도 기록 열람이 가능해야 해서 로컬 저장소를 도입했고,
스키마가 단순하고 iOS 17 이상 타깃이라 CoreData 대신 SwiftData를 선택했습니다.
마이그레이션 리스크는 VersionedSchema로 대비했습니다."

패턴 보이지? 맥락(왜) + 선택(무엇) + 근거(대안 비교) + 결과(수치).
이 네 박자만 지키면 어떤 기술이든 설득력이 생겨.

10. 면접에서 무너지지 않으려면

포트폴리오의 마지막 관문은 "본인이 쓴 코드를 방어할 수 있는가"야.
그래서 제출 전에 스스로 이 질문들에 답해봐.

☑ 이 프로젝트에서 가장 어려웠던 문제와 해결 과정을 3분 안에 말할 수 있나?

☑ 지금 다시 만든다면 무엇을 바꿀 건가?

☑ 여기서 쓴 라이브러리를 직접 구현한다면 어떻게 할 건가?

☑ 메모리 누수가 생길 수 있는 지점은 어디인가? ([weak self] 위치 설명)

☑ 이 화면의 렌더링 성능을 측정해봤나? 어떤 도구로?

☑ 사용자가 10만 명이 되면 어디부터 터질 것 같나?

여기서 "지금 다시 만든다면" 질문이 특히 중요해.
자기 코드의 부족함을 스스로 짚는 사람은 리뷰를 받아들일 사람으로 보이거든.
팀 입장에서 이건 기술력만큼 중요한 조건이야.

참고로 요즘은 재능넷 같은 재능 공유 플랫폼에서 현업 iOS 개발자에게
포트폴리오 코드 리뷰나 모의 면접을 받아보는 주니어들도 많아.
혼자 보면 안 보이던 구멍이 남의 눈엔 3분 만에 보이는 법이니까,
제출 전에 한 번쯤 외부 시선을 거치는 건 꽤 효율적인 투자야.

11. 흔한 지뢰 모음 (이건 진짜 피하자)

💣 API 키를 그대로 커밋 — 보안 감수성 제로로 보임. xcconfig + .gitignore 필수.

💣 강제 언래핑(!) 도배 — 크래시 유발 코드. 옵셔널 바인딩이나 guard 사용.

💣 print 디버깅 잔해 — 정리 안 된 코드 인상. OSLog로 전환하면 오히려 가점.

💣 주석 처리된 죽은 코드 덩어리 — Git 있는데 왜 남겨두나요.

💣 Assets에 안 쓰는 이미지 50장 — 앱 용량과 관리 감각.

💣 README에 "열심히 했습니다" — 감상문 말고 기록을 쓰자.

💣 빌드 자체가 안 됨 — 최소 3년치 Xcode 버전 호환 여부, 최소 타깃 명시.

12. 4주 완성 실행 로드맵

"알겠는데 뭐부터 해요?" 하는 사람을 위한 현실 일정표.

주차할 일산출물
1주차기존 프로젝트 1개 선정 → 아키텍처 리팩터링, 폴더 구조 정리깔끔한 계층 구조
2주차네트워크 레이어 프로토콜화 + 유닛 테스트 8~12개 작성테스트 통과 배지
3주차Instruments로 성능 측정 → 개선 → 전후 수치 기록트러블슈팅 문서
4주차README 전면 재작성 + 다이어그램 + TestFlight 배포완성된 포트폴리오

새 앱을 만드는 것보다 있는 앱을 제대로 끝까지 밀어붙이는 게 훨씬 빠르고 강력해.
0에서 1은 오래 걸리지만, 1에서 10은 4주면 되거든.

마무리 — 결국 보여줘야 하는 건 '사고 과정'

정리해보자.

1️⃣ 개수보다 깊이. 메인 1개에 모든 걸 쏟아라.
2️⃣ README가 첫인상이자 거의 전부다.
3️⃣ 모든 기술 선택엔 "왜"와 "대안 비교"를 붙여라.
4️⃣ 테스트 코드는 가성비 최고의 차별화 포인트.
5️⃣ 커밋 로그는 못 베끼는 성실함의 증거.
6️⃣ 배포까지 해본 사람은 다른 리그다.
7️⃣ 수치로 말해라. "빨라졌다" 대신 "62% → 18%".

포트폴리오는 완성품 전시회가 아니라 네 머릿속을 보여주는 창문이야.
화려한 UI보다, 문제를 만나서 고민하고 근거를 들어 선택한 흔적이 훨씬 오래 남아.

그러니까 오늘은 새 프로젝트 만들지 말고,
이미 있는 레포의 README부터 열어보자. 거기가 시작이야. 🚀

화이팅! 다음에 또 유용한 Swift 이야기로 돌아올게 👋

댓글 작성

이 글에 대한 여러분의 생각을 들려주세요

댓글 0