휴직 후 복귀, AI와 함께 다시 성장하기까지

휴직 후 복귀, AI와 함께 다시 성장하기까지

Tech

Overview

안녕하세요, Backend Engineer Ellen입니다. 육아 휴직을 갖고 복귀했을 때, 가장 먼저 들었던 생각은 “공백 기간 동안 얼마나 많은 것들이 변했을까?”였습니다.

그리고 실제로 마주한 숨고의 개발 문화는 제가 예상했던 것 이상으로 훨씬 더 성숙하고 체계적으로 발전되었고, 특히 AI 활용 방식의 변화가 팀의 속도를 완전히 바꿔놓고 있었습니다.

이 글에서는 복귀 이후 경험한 AI 기반 개발 문화의 실제 사례, 업무 수행 방식의 변화, 그리고 실무 인사이트를 공유하고자 합니다.

복귀 첫날 느낀 가장 큰 변화

가장 크게 체감한 개발 환경은 AI를 단순한 도구로 사용하는 것이 아닌, 문제를 정의하고 해결하는 팀 전체의 사고방식을 바꾸는 데에 사용한다는 것이었습니다. 장애가 발생하면 팀원들은 자연스럽게 이렇게 말하곤 했습니다.

“GPT한테 관점 한번 받아볼까요?”

이는 AI에게 단순히 문제를 ‘맡긴다’라는 뜻이 아니라, 문제를 더 넓은 시각으로 해석하는 도구로 활용한다는 의미였습니다. AI는 개발 과정 전체에 자연스럽게 녹아들어 있었지만, 최종 판단과 품질 기준은 여전히 사람의 경험과 검증에 기반하고 있었습니다.

좋은 팀은 도구를 ‘많이’ 쓰는 팀이 아니라, 도구를 통해 스스로를 ‘더 성장시키는’ 팀이다.

1. AI 기반 개발 프로세스로의 전환

예전에는 버그가 생기면 로그를 직접 추적하고 Stack Overflow에서 비슷한 사례를 찾는 데 많은 시간이 들었습니다. 테스트 코드 작성은 기능 개발보다 오래 걸리는 경우도 많았죠.

하지만 지금은 팀의 방식이 달라졌습니다.

복귀 전과 비교하면 개발 프로세스는 다음과 같이 변화했습니다.

[과거]

  • 장애 발생 시: 로그 추적 → 검색 → 주변 도움 요청
  • 테스트 코드: 모든 케이스를 사람이 0부터 작성
  • 리팩토링: 코드 구조·의존성을 모두 수동 분석

[현재 (AI 활용 후)]

  • AI가 먼저 에러 흐름과 원인 후보를 요약 → 개발자가 검증
  • 테스트 코드의 스캐폴딩과 기본 케이스는 AI가 생성 → 개발자가 확장
  • 리팩토링 방향성(분리 포인트, 복잡도 높은 함수 탐지 등)을 AI가 제안

즉, AI가 초안을 만들고 사람의 통찰이 완성한다는 흐름이 자리 잡았습니다

1. 실무에서 경험한 AI 협업 사례

1-1. 로그 분석 — '스택트레이스를 문서처럼 정리해준다'

대체 이미지

복잡한 스택트레이스를 AI에게 전달하면 다음과 같은 결과를 즉시 받아볼 수 있습니다.

  • 에러 발생 지점
  • 상위/하위 호출 관계
  • 재현 조건 및 입력값 특징
  • 의심되는 라이브러리/버전 이슈
  • 해결 전략 후보 (롤백, 패치, 방어 로직 추가 등)

이 과정으로 원인 후보군을 좁히는 속도가 기존 대비 3~5배 개선되었습니다. AI가 “추적을 대신해 주는 도구”라기보다, '문제를 구조화해 주는 도구'로서 활용할 수 있다는 것을 깨달았던 지점이었습니다.

1-2. 테스트 코드 자동 스캐폴딩 — 구체적인 예시

테스트 코드는 ‘있어야 하는 건 알지만, 항상 시간이 부족한 영역’입니다. AI를 활용하면서 테스트 코드 작성의 진입 장벽을 눈에 띄게 낮출 수 있었습니다.

여기에는 두 가지 핵심 요소가 있습니다.

  1. 요구사항을 얼마나 명확히 프롬프트에 담느냐
  2. AI가 만든 초안을 어떻게 “우리 서비스에 맞게” 끌어올리느냐

아래는 Flask 기반 /users/<id> 프로필 조회 API에 대한 실제 형태의 예시입니다. (코드는 개념 전달을 위해 단순화했습니다.)

(1) 테스트 요구사항 정의

먼저, 사람(개발자)이 해야 할 일은 “테스트하고 싶은 관점”을 명확하게 정의하는 것입니다.

  1. 엔드포인트: GET /users/<id>
  2. 기대 동작:
  • 존재하는 사용자 ID → 200 응답 + JSON에 name 필드 존재
  • 존재하지 않는 사용자 ID → 404 응답
  1. 프레임워크: Flask + unittest
  2. 제약 사항: DB는 테스트용 SQLite, 인증은 고려하지 않음(예시 단순화 목적)

(2) 프롬프트 예시

Flask 기반 REST API에서 /users/ 프로필 조회 엔드포인트에 대한 unittest 기반 테스트 코드를 작성하려고 합니다.

조건은 다음과 같습니다.

  1. 프레임워크 : Flask + unittest
  2. 엔드포인트: GET /users/<id>
  3. 요구사항:
  • 정상 시나리오 : 존재하는 사용자 ID에 대해 200을 응답하고, JSON body에 "name" 필드가 포함되어야 합니다.
  • 예외 시나리오 : 존재하지 않는 사용자 ID에 대해서는 404를 응답해야 합니다.
  1. 테스트 클라이언트는 Flask의 test_client()를 사용해 주세요.
  2. setUp에서 테스트용 앱 컨텍스트와 DB 초기화를 포함해 주세요
  3. tearDown에서 DB 정리를 호출하는 형태로 작성해 주세요.

위 조건을 만족하는 unittest TestCase 클래스를 작성해 주세요.


이 정도 수준으로 맥락과 제약 조건을 명확히 주면, AI는 더 이상 “예제용 코드”가 아니라, **실제 서비스에 바로 적용 가능한 구조**를 제안합니다.

(3) AI가 생성한 테스트 코드 초안 (예시)

import unittest from app import create_app, db from app.models import User class UserProfileTestCase(unittest.TestCase): def setUp(self): self.app = create_app("testing") self.app_context = self.app.app_context() self.app_context.push() db.create_all() # 테스트용 사용자 데이터 생성 self.user = User(name="Test User", email="test@example.com") db.session.add(self.user) db.session.commit() self.client = self.app.test_client() def tearDown(self): db.session.remove() db.drop_all() self.app_context.pop() def test_get_user_profile_success(self): response = self.client.get(f"/users/{self.user.id}") self.assertEqual(response.status_code, 200) data = response.get_json() self.assertIn("name", data) self.assertEqual(data["name"], "Test User") def test_get_user_profile_not_found(self): response = self.client.get("/users/999999")

(4) 실제로는 이렇게 보완했다

실제 서비스에 적용할 때는 대부분 아래와 같은 항목을 추가하게 됩니다.

  • 인증/인가 헤더 (Authorization 헤더, 토큰 유효성 체크 등)
  • 멀티 테넌트/도메인 제약 (예: 특정 사업자만 접근 가능)
  • 응답 스키마 검증 (필드 유효성, 타입, 포맷 등)
  • 로깅/트레이싱 키 존재 여부

AI가 여기까지 완벽히 알고 있을 수는 없습니다.

AI는 '뼈대와 기본 흐름을 제공'하고, '도메인 지식과 품질 기준을 반영해 완성하는 일은 개발자의 몫'입니다.

1-3. 기능 개발 사례 — 회원 탈퇴 API와 테스트

회원 탈퇴 API를 구현할 때도 AI를 적극 활용했습니다. 설계, 구현, 테스트까지 단계별로 AI를 어떻게 썼는지 살펴보겠습니다.

(1) 설계 단계 프롬프트

Flask 기반 REST API에서 "회원 탈퇴" 기능을 설계하려고 합니다. 요구사항은 다음과 같습니다.

  1. HTTP API :
  • 엔드포인트: POST /users//deactivate
  • 인증된 사용자만 호출 가능
  1. 기능
  • 사용자 is_active 플래그를 False로 변경 (논리적 삭제)
  • 관련 세션/토큰을 모두 무효화
  • 외부 연동 서비스(예: 마케팅/알림 시스템)에 사용자 비활성화 이벤트 전파
  1. 비기능 요구사항:
  • 트랜잭션 경계 내에서 데이터 정합성이 보장되어야 합니다.
  • 외부 연동 API 실패 시 재시도 또는 보상 로직이 필요합니다.
  • GDPR 관점에서 실제 데이터 삭제는 별도 배치로 처리합니다.

위 조건을 바탕으로:

  1. 주요 처리 단계(순서)를 나열하고,
  2. 트랜잭션/에러 핸들링 관점에서 체크해야 할 항목을 체크리스트 형태로 정리해 주세요.

이런 식의 프롬프트를 사용하면, AI는 단순 코드가 아니라 설계 리뷰에 가까운 응답을 제공합니다.

  • 비활성화 vs 삭제 전략 비교
  • 외부 연동 실패 시 보상/재시도 전략 제안
  • 트랜잭션 범위 및 롤백 시나리오 제안
  • 감사 로그 / 보안 로그 필요성 언급

덕분에 동료 리뷰 전에 “사각지대 후보”를 먼저 제거하는 효과가 있었고, 리뷰는 '이 방향이 우리 서비스에 맞는가?' 라는 조금 더 생산적인 주제에 집중할 수 있었습니다.

(2) 구현 단계 — AI로부터 받은 기본 코드 뼈대

AI가 제안한 Flask 핸들러 초안은 대략 아래와 같은 형태였습니다. (역시 단순화된 예시입니다.)

from flask import Blueprint, jsonify, request from app import db from app.models import User from app.auth import login_required, current_user from app.integrations import notify_user_deactivated from app.tokens import invalidate_tokens_for_user bp = Blueprint("users", __name__) @bp.route("/users/<int:user_id>/deactivate", methods=["POST"]) @login_required def deactivate_user(user_id): if current_user.id != user_id: return jsonify({"error": "Forbidden"}), 403 user = User.query.get(user_id) if not user: return jsonify({"error": "User not found"}), 404 if not user.is_active: return jsonify({"message": "Already deactivated"}), 200 try: user.is_active = False db.session.commit() # 세션/토큰 무효화 invalidate_tokens_for_user(user_id) # 외부 연동 알림 (: 마케팅/푸시 시스템) notify_user_deactivated(user_id) except Exception as e: db.session.rollback() # 실제 서비스에서는 더 구체적인 예외 처리/로깅 필요 return jsonify({"error": "Deactivation failed"}), 500 return "", 204

이 코드는 완성본이라고 보기엔 부족하지만, “핸들러 구조 + 기본 분기 + 응답 코드”라는 큰 틀을 빠르게 만들어주는 데는 충분했습니다.

실제 서비스에서는 여기에:

  • 도메인별 예외 타입 정의
  • 외부 연동의 장애 허용 범위 설정
  • 비동기 처리/이벤트 기반 분리 여부 결정
  • 감사 로그/보안 로그 추가

같은 요소들을 추가로 설계·구현하게 됩니다.

대체 이미지

3) 테스트 코드 — 다시, 프롬프트와 예시

설계와 구현 뒤에는 “보호막 역할”을 하는 테스트가 필요합니다.

[테스트 요구사항]

  • 활성화된 사용자가 탈퇴를 요청하면 204 반환 + is_active=False
  • 비존재 사용자 요청 시 404
  • 다른 사용자가 남의 계정 탈퇴 요청 시 403
  • 이미 비활성화된 사용자는 200 + 메시지 반환

프롬프트 예시

아래 Flask 뷰 함수 deactivate_user에 대한 테스트 코드를 작성하려고 합니다. 조건은 다음과 같습니다.

  1. 테스트 프레임워크: unittest
  2. 인증은 login_required 데코레이터와 current_user를 사용하는 구조입니다.
  3. 시나리오:
  • 활성 사용자 본인이 탈퇴 요청 → 204 응답, DB에서 is_active=False로 변경
  • 존재하지 않는 사용자 ID로 요청 → 404 응답
  • 다른 사용자가 탈퇴를 시도 → 403 응답
  • 이미 비활성화된 사용자 → 200 응답과 "Already deactivated" 메시지

테스트 코드에서:

  • 테스트용 앱/DB 초기화는 setUp에서 처리해 주세요.
  • current_user는 테스트 내에서 patch를 사용해 모킹해 주세요.
  • 각 테스트 케이스마다 독립적으로 검증이 가능하도록 작성해 주세요. 위 요구사항을 만족하는 unittest 기반 TestCase 코드를 작성해 주세요.

AI가 제안한 테스트 코드 예시 (요약 버전)

import unittest from unittest.mock import patch from app import create_app, db from app.models import User class DeactivateUserTestCase(unittest.TestCase): def setUp(self): self.app = create_app("testing") self.app_context = self.app.app_context() self.app_context.push() db.create_all() self.active_user = User(email="active@example.com", is_active=True) self.inactive_user = User(email="inactive@example.com", is_active=False) db.session.add_all([self.active_user, self.inactive_user]) db.session.commit() self.client = self.app.test_client() def tearDown(self): db.session.remove() db.drop_all() self.app_context.pop() @patch("app.views.users.current_user") def test_deactivate_self_success(self, mock_current_user): mock_current_user.id = self.active_user.id response = self.client.post(f"/users/{self.active_user.id}/deactivate") self.assertEqual(response.status_code, 204) user = User.query.get(self.active_user.id) self.assertFalse(user.is_active) @patch("app.views.users.current_user") def test_deactivate_not_found(self, mock_current_user): mock_current_user.id = self.active_user.id response = self.client.post("/users/999999/deactivate") self.assertEqual(response.status_code, 404) @patch("app.views.users.current_user") def test_deactivate_other_user_forbidden(self, mock_current_user): mock_current_user.id = 999999 response = self.client.post(f"/users/{self.active_user.id}/deactivate") self.assertEqual(response.status_code, 403) @patch("app.views.users.current_user") def test_deactivate_already_inactive(self, mock_current_user): mock_current_user.id = self.inactive_user.id response = self.client.post( f"/users/{self.inactive_user.id}/deactivate" ) self.assertEqual(response.status_code, 200) self.assertIn("Already deactivated", response.get_data(as_text=True))

이 정도 코드가 “AI 초안”으로 바로 나온다면, 개발자는 여기서 도메인 특화 검증, 추가적인 예외 케이스, 보안 관련 관점을 덧붙이는 데 집중할 수 있습니다.

2. Before & After — AI가 바꾼 건 생산성보다 ‘사고방식’

AI는 생산성만 향상해 준 것이 아니라 문제를 바라보는 사고방식을 변화시켜 준 점은 제게 매우 큰 인상을 남겨 주었습니다. AI가 개발자 대신 생각해 주는 것이 아니라, 빠른 시작과 관점을 제시해 주는 동료로 작동했기 때문입니다.

구분기존 방식AI 활용 후
설계 검토개발자 리뷰 위주, 사각지대 발생AI 체크리스트 + 사람 검증
API 초안boilerplate 반복 작성명확한 요구만 주면 구조 자동 생성
테스트 코드작성 비용이 가장 큼스캐폴딩 자동화 → 50% 이상 절약
리팩토링코드 의존성 분석에 시간 소모AI가 함수 구조·복잡도 분석 후 리팩토링 제안
로그/에러 분석로그/검색/실험 반복AI가 재현 조건·원인 후보를 구조화
문서화후순위로 밀리며 종종 누락초안 자동 생성 → 검토 중심 작업
온보딩도메인 학습에 많은 시간 소요모듈 요약, 핵심 구조 분석 등 자동으로 요약 단축
대체 이미지

2. AI와 함께 일하며 배운 것들

2-1. AI는 ‘탐색’과 ‘초안 작성’에 탁월하다

특히 아래 영역에서 효과가 컸습니다.

  • 로그 분석: 원인 후보·재현 조건 자동 요약
  • 리팩토링: 복잡도 높은 함수 및 분리 포인트 추천
  • 테스트 코드: 스캐폴딩 자동 생성으로 작성 시간 대폭 감소

AI는 '문제 해결의 시작점을 빠르게 만들어주는 도구'였습니다.

2-2. 최종 품질은 AI가 아닌 사람이 보장한다

AI가 제안하는 코드는 겉보기에 완성된 형태로 보일 수 있지만, 다음과 같은 요소는 대부분 사람이 직접 검토해야 합니다.

  • 트랜잭션 안정성
  • 동시성/경합 상황
  • 외부 시스템 장애 시 복구 시나리오
  • 실제 도메인 규칙 및 법적/정책적 요구사항

AI는 도우미이지, 품질 보증 도구가 아닙니다. 최종 안정성과 신뢰성은 결국 개발자의 책임입니다.

2-3. 프롬프트 엔지니어링은 곧 개발 역량

좋은 프롬프트는 단순한 “질문 능력”이 아니라, 문제를 정확하게 정의하는 능력과 직결됩니다.

  • 어떤 환경에서
  • 어떤 제약 조건 아래
  • 무엇을 검증하고 싶은지

를 분명하게 표현할수록, AI의 답변은 더 깊어지고 구체적으로 됩니다.

3. 변화는 두려움이 아닌 기회였다

복귀 초기에는 솔직히 두려웠습니다.

“AI가 개발자의 역할을 줄이는 건 아닐까?”

하지만 실제로 경험한 변화는 정반대였습니다.

  • 반복적이고 패턴화된 작업 → AI가 대신 처리
  • 설계·리뷰·커뮤니케이션 → 개발자가 더 많은 시간을 투자

AI는 개발자를 대체하는 것이 아니라 개발자가 본질에 집중하도록 돕는다.

Closing

휴직 이후 복귀했을 때의 불안은 지금은 새로운 성장의 계기가 되었습니다. AI와 함께 일하는 방식은 개발자의 역할을 축소하는 것이 아니라, 개발자가 책임져야 할 깊이를 확장합니다.

앞으로도 저는 AI를 단순한 도구가 아니라 함께 고민하고 탐색하는 동료로 삼아, 더 나은 개발 문화와 경험을 만들어가고 싶습니다.

  • #ai
  • #ax
  • #backend
  • #chat gpt
  • #o2o
  • #python
  • #soomgo
Ellen Shin
Ellen Shin Backend Engineer

모두의 더 나은 삶을 위해
함께 변화를 만들어갈 동료를 기다립니다

채용중인 공고 보기