GROWTH LOG STORIES

함께 만든 성장을,
기록으로 연결합니다.

AI와 함께 배우고, 직접 만들고, 서로 나눈
그로스로그 멤버들의 생생한 성장 기록을 만나보세요.

성장 기록
5기+이어진 커뮤니티
HEALTHIO
PROJECT

사진 한 장으로 시작하는 건강 루틴, Health IO

대표 이야기 읽기

ALL STORIES

성장의 순간을
한곳에서 만나보세요.

프로젝트의 시작부터 모임에서 나눈 인사이트까지,
그로스로그가 함께 만든 기록을 모았습니다.
개의 성장 기록

성장일지

[GL 2기] 테스트 없는 회사에서 혼자 시작한 단위 테스트, 그리고 크리스마스 이브의 베타 출시 (성장일지 8회차 큐레이션) 🌱

GROWTH LOG🌱 2026. 8. 7. 04:32

안녕하세요! 그로스로그 입니다!😊 🌱

"일단 나라도 깨끗하게 해보자"로 시작한 테스트 공부, 그리고 3개월을 달려 크리스마스 이브에 내놓은 베타 서비스. 2기 8회차입니다.

성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 매 회차 운영진이 모든 글을 읽고, 그중 특히 마음에 남은 글을 함께 골라 큐레이션합니다.

 

01

🧪 JUnit 단위 테스트 어노테이션 정리

이태형 님

이런 내용이에요
시작 문장이 솔직합니다. "회사는 테스트 코드를 짜지 않고 바로 개발하기 때문에 코드가 지저분하고, 메서드가 길어져 다른 사람 코드 보기가 힘들다."
그래서 "일단 나라도 깨끗하게 해보자"며 클린 코드와 TDD를 찾아보기 시작한 기록이에요.
Mock, MockMvc, Mockito.mock(), @Mock, @InjectMocks, @MockBean을 각각의 역할과 차이까지 짚어 정리했습니다.

테스트 문화가 없는 팀에서 혼자 테스트를 시작하는 건 생각보다 큰 결심입니다. 아무도 요구하지 않고, 아무도 알아주지 않는 일이니까요.

조금 더 들어가 보면

단위 테스트에서 가장 먼저 부딪히는 벽이 의존성입니다.

어떤 서비스 하나를 테스트하고 싶은데, 그 서비스가 리포지토리를 부르고, 리포지토리가 DB를 부르고, 옆에서 외부 API도 호출합니다. 하나를 테스트하려면 전부가 살아 있어야 하는 상황이 되죠.

그러면 테스트가 이렇게 됩니다. DB가 꺼져 있으면 실패하고, 외부 API가 느리면 느려지고, 어제 넣은 데이터가 남아 있으면 결과가 달라집니다. 내 코드는 멀쩡한데 테스트가 빨간불인 상황이요.

Mock(가짜 객체)이 여기서 나옵니다. 진짜 대신 세워두는 대역이에요. "이 메서드를 부르면 111을 돌려줘"라고 미리 정해두면, DB 없이도 그 상황을 만들 수 있습니다.

핵심은 무엇을 테스트하려는지 분명해진다는 점입니다. 서비스의 계산 로직을 확인하고 싶은 거지 DB가 잘 도는지 보려는 게 아니잖아요. 관심 없는 부분을 가짜로 세워두면 진짜 보고 싶은 것만 남습니다.

📝 원문에서는 이렇게 씁니다

가장 기본적인 형태입니다.

@Test
public void UserRepositoryTest() {
    // UserRepository를 mocking하고 count()가 실행되면 111L을 리턴한다
    UserRepository localMockRepository = Mockito.mock(UserRepository.class);
    Mockito.when(localMockRepository.count()).thenReturn(111L);

    long userCount = localMockRepository.count();

    Assert.assertEquals(111L, userCount);
    Mockito.verify(localMockRepository).count();
}

읽는 순서 그대로입니다. 가짜를 만들고 → 어떻게 답할지 정하고 → 불러보고 → 결과를 확인합니다.

마지막 줄의 verify가 조금 다른 일을 합니다. 결과값이 아니라 "이 메서드가 실제로 호출되었는가"를 확인해요. 값을 돌려주지 않는 메서드(알림 전송, 로그 기록 같은 것)를 테스트할 때 이게 필요합니다. 결과가 없으니 "불렀는지"로 확인하는 거죠.

이걸 어노테이션으로 줄인 게 @Mock입니다.

@ExtendWith(MockitoExtension.class)
public class MockAnnotationUnitTest {

    @Mock
    UserRepository mockRepository;

    @Test
    public void givenCountMethodMocked_WhenCountInvoked_ThenMockValueReturned() {
        Mockito.when(mockRepository.count()).thenReturn(123L);

        long userCount = mockRepository.count();

        Assert.assertEquals(123L, userCount);
        Mockito.verify(mockRepository).count();
    }
}

글이 짚은 것처럼 @MockMockito.mock()의 축약형이고, JUnit5에서는 @ExtendWith(MockitoExtension.class)로 활성화해야 동작합니다. 이 한 줄을 빠뜨려서 "왜 목이 null이지"로 헤매는 일이 흔해요.

🧭 @Mock@MockBean, 이 둘을 가르는 것

이 글에서 가장 값진 부분은 비슷해 보이는 두 어노테이션의 차이를 짚은 대목입니다.

@Mock스프링과 아무 상관이 없습니다. 순수한 자바 객체를 하나 만들어 세워두는 것뿐이에요. 애플리케이션 컨텍스트가 아예 뜨지 않습니다. 그래서 빠릅니다.

@MockBean은 다릅니다. 스프링 컨테이너 안에 가짜 빈을 등록해요. 같은 타입의 진짜 빈이 있으면 그걸 대체하고, 없으면 새로 추가합니다. 컨트롤러 테스트에서 서비스만 가짜로 바꾸고 싶을 때 쓰는 방식이죠.

@MockBean
private LogManageService logManageService;

@Test
public void testService() {
    Mockito.when(logManageService.getAccessControlDetailLog(1))
           .thenReturn(MPAccessControlLog.DetailLog.builder().build());
}

글이 마지막에 부작용까지 적어둔 게 좋았습니다. @MockBean은 애플리케이션 컨텍스트에 손을 대기 때문에 테스트가 느려질 수 있다고요.

여기엔 잘 안 알려진 이유가 하나 더 있습니다. 스프링은 테스트 사이에 컨텍스트를 재사용하는데, @MockBean 구성이 조금이라도 다르면 컨텍스트를 새로 띄웁니다. 테스트 클래스마다 다르게 쓰면 그만큼 초기화가 반복돼요. 테스트 100개가 30초에서 3분이 되는 게 대개 이 지점입니다.

그래서 실무 기준은 대략 이렇습니다. 로직만 볼 거면 @Mock, 스프링이 개입하는 동작(요청 매핑, 직렬화, 필터 등)을 봐야 하면 @MockBean. 글이 "테스트 격리 / 성능 향상 / 특정 동작 제어 / 의존성 문제 해결"로 사용 이유를 나눈 것도 같은 맥락이에요.

큐레이터 노트

"일단 나라도"라는 문장 때문에 뽑았습니다.

테스트를 안 짜는 팀에서 혼자 테스트를 시작하는 건 티가 안 나는 일입니다. 기능이 하나 더 나오는 것도 아니고, 당장 누가 알아주지도 않아요. 그런데 바꿀 수 없는 환경 안에서 자기가 통제할 수 있는 범위를 찾아낸 판단입니다. 이게 오래 성장하는 사람의 방식이에요.

정리의 밀도도 좋았습니다. 어노테이션을 나열만 한 게 아니라 @Mock@MockBean처럼 헷갈리는 짝을 골라 차이를 설명했어요. 실제로 쓰다가 헷갈려본 사람만 이 순서로 정리합니다.

그리고 부작용까지 적은 정리는 드뭅니다. 대부분의 정리 글은 "이렇게 쓰면 됩니다"에서 끝나요. 단점을 함께 적었다는 건 선택할 수 있는 지식으로 만들었다는 뜻입니다.

원문 읽으러 가기  ↗velog.io/@thlee98/%EC%84%B1%EC%9E%A5%EC%9D%BC%EC%A7%80-8%E…
 

02

🎄 베타버전 출시

이민경 님

이민경 님 원문에 실린 이미지
이런 내용이에요
크리스마스 이브에 베타 서비스를 출시한 기록입니다. 만성질환 반려동물의 건강 관리와 보호자의 심리적 공감을 함께 다루는 서비스예요.
초보 개발자 두 명이 10월 1일에 시작해 약 3개월 만에 내놓았습니다. 저자가 맡은 건 백엔드 전체, 커뮤니티 탭 프론트엔드, 배포 전 디버깅이었고요.
글 첫머리에 "오늘은 기운이 빠져서 일기처럼 적어둔다"고 적혀 있습니다. 출시 직후의 상태가 그대로 남은 기록이에요.

기술 정리 글이 아닙니다. 그런데 이 회차에서 가장 오래 마음에 남은 글이었어요.

조금 더 들어가 보면

먼저 만든 것의 규모를 보면 좋겠습니다. 글에 적어둔 키워드가 이렇습니다.

· 프론트엔드 — React, TypeScript, Tailwind, WebSocket, STOMP, HTTPS

· 백엔드 — Spring Boot, Java, MySQL, EC2, SSL

두 명이서 3개월에 여기까지 갔습니다. 게다가 WebSocket과 STOMP가 들어 있어요. 실시간 채팅이 있다는 뜻입니다.

실시간 기능은 난이도가 한 단계 다릅니다. 일반적인 요청-응답은 물어보고 답을 받으면 끝이지만, 웹소켓은 연결을 계속 붙들고 있어야 해요. 끊기면 다시 이어야 하고, 그사이 놓친 메시지도 챙겨야 하고, 여러 사람이 같은 방에 있을 때 누구에게 보낼지도 정해야 합니다. 초보 개발자 둘이서 이걸 붙여 배포까지 갔다는 게 가볍지 않아요.

SSL 적용도 처음 해봤다고 적혀 있습니다. 로컬에서는 아무 문제 없던 서비스가 도메인을 붙이는 순간 인증서, 리다이렉트, 혼합 콘텐츠 문제로 발목을 잡는 단계예요. "만드는 것"과 "내놓는 것" 사이에 있는 벽입니다.

🧭 글에 담긴 세 가지 배움

이 글은 회고지만 배운 것이 구체적으로 적혀 있습니다.

첫째, 설계의 중요성. "이 과정에서 정말 개발 설계의 중요성을 깨달았다"고 씁니다. 프로젝트 초반에는 설계가 왜 필요한지 잘 안 와닿아요. 필요성은 중반 이후, 뭔가를 바꾸려는데 여기저기가 딸려 나올 때 몸으로 옵니다. 그 시점을 지나온 문장입니다.

둘째, 이론과 실전의 간격. "방통대 공부도 정말 도움이 되지만 이론 위주이고, 프로젝트 진행은 또 다른 느낌"이라고요. 정확한 관찰입니다. 이론은 답이 있는 문제를 다루고, 프로젝트는 문제 자체를 정의해야 하는 상황을 줍니다. 둘 다 필요하고, 서로를 대체하지 못해요.

셋째, 에러를 찾는 방법. 이 문장이 특히 좋았습니다.

"에러를 찾을 땐, 단계적으로, 이 동작을 눌렀을 때 어떻게 흘러가는지 로직을 살피면서 하기."

디버깅을 많이 해본 사람이 도달하는 결론입니다. 초보일수록 에러 메시지를 검색창에 넣는 것부터 하는데, 그건 남의 상황에서 나온 답이에요. 내 시스템에서 요청이 어디를 지나는지 따라가는 것이 결국 빠릅니다. 3개월 동안 몸으로 배운 방법을 한 줄로 적어둔 거죠.

그리고 마지막에 하나 더 있습니다. "개발도 체력이다."

🧭 "회복도 성장인가"라는 질문

글 맨 앞의 문장을 다시 보고 싶습니다.

"성장일지지만, 오늘은 기운이 빠져서 일기처럼 적어둔다. 성장도 회복을 해야... 하는 것이 아닐까?"

성장 기록을 쓰는 활동을 하다 보면 잘한 것만 써야 할 것 같은 압박이 생깁니다. 새로 배운 기술, 해결한 문제, 읽은 책. 그래서 지친 주에는 아예 안 쓰게 되기도 해요.

그런데 이 글은 지친 상태 그대로 썼습니다. 그래서 3개월 프로젝트의 끝이 어떤 모습인지가 남았어요. 결과물은 나왔는데 기쁨보다 피로가 크고, 다음에 뭘 할지 정하지 못했고, 고칠 게 많이 보이는 상태. 프로젝트를 끝까지 해본 사람이라면 다 아는 그 자리입니다.

이 기록이 나중에 더 값집니다. 잘 굴러갈 때 쓴 글보다, 힘들 때 쓴 글이 그 시기를 정확하게 남기거든요.

큐레이터 노트

끝까지 갔고, 그 끝을 솔직하게 적었기 때문에 뽑았습니다.

시작한 프로젝트를 실제로 내놓는 사람은 생각보다 적습니다. 대부분은 80%쯤에서 멈춰요. 배포가 남았고, 도메인이 남았고, 버그가 남았고… 그러다 흐지부지됩니다. 12월 24일을 스스로 정해두고 "일단 던져본" 판단이 이 프로젝트를 완성시켰습니다.

"계속 고치다간 베타버전보다 2025년이 먼저 오겠다"는 문장이 그걸 잘 보여줘요. 베타는 완성이 아니라 기준선을 세우는 일이라는 걸 스스로 정리한 겁니다.

그리고 글 중간의 "혹시 저와 함께 하실 분...?"이 반가웠습니다. 성장일지는 혼자 쓰는 기록이지만, 이렇게 읽는 사람에게 말을 거는 순간 커뮤니티가 됩니다. 그로스로그가 바라는 모습이기도 하고요.

푹 쉬시고, 회복도 성장의 일부입니다. 😊

원문 읽으러 가기  ↗velog.io/@gaonlee/%EB%B2%A0%ED%83%80%EB%B2%84%EC%A0%84-%EC…

💡 두 편을 겹쳐 읽으며

한 편은 정리 글이고, 한 편은 일기에 가깝습니다. 그런데 두 글의 시작 지점이 같습니다.

둘 다 "지금 상황이 이상적이지 않다"에서 출발합니다.

이태형 님의 회사에는 테스트 문화가 없습니다. 이민경 님의 팀은 초보 개발자 두 명뿐이고요. 둘 다 좋은 조건이 아닙니다.

그런데 두 사람이 한 일이 같습니다. 환경을 탓하는 대신 자기가 할 수 있는 범위를 정했어요. 한 사람은 "일단 나라도 깨끗하게", 다른 한 사람은 "12월 24일에 일단 던지기".

완벽한 조건이 갖춰지길 기다렸다면 둘 다 아무것도 못 했을 겁니다. 팀 전체가 테스트를 도입할 때까지 기다리거나, 팀원이 더 모일 때까지 기다리거나요. 대신 각자 지금 손에 쥔 것으로 시작할 수 있는 크기를 찾았습니다.

그리고 두 글 모두 다음이 있습니다. 한쪽은 테스트를 익혀 팀에 제안할 준비를, 다른 쪽은 회복하고 다음 방향을 정할 시간을요.

성장일지가 2주에 한 번인 이유이기도 합니다. 한 번에 크게 나아가지 않아도 됩니다. 다음 회차가 또 오니까요.

이번 회차에서 가져갈 것

1. 바꿀 수 없는 환경에서 바꿀 수 있는 범위 찾기. 팀 문화는 혼자 못 바꿉니다. 그런데 내가 짜는 코드 한 덩어리는 바꿀 수 있어요. 거기서 쌓인 게 나중에 제안할 근거가 됩니다.

2. 테스트는 "무엇을 안 볼지"부터 정하기. 단위 테스트의 시작은 확인할 대상이 아니라 가짜로 세울 대상을 고르는 일입니다. DB, 외부 API, 무거운 빈. 관심 밖의 것을 걷어내야 진짜 보고 싶은 게 남습니다.

3. 목(mock)에도 비용이 있다. @Mock은 컨텍스트 없이 빠르고, @MockBean은 스프링을 띄웁니다. 로직만 볼 거면 앞의 것, 스프링 동작까지 봐야 하면 뒤의 것. 무심코 섞어 쓰면 테스트 전체가 느려집니다.

4. 마감 날짜를 스스로 정하기. 고칠 것은 끝없이 나옵니다. "이날 내놓는다"를 먼저 박아두면 무엇이 진짜 필요한지가 저절로 걸러져요. 베타는 완성이 아니라 시작점입니다.

5. 에러는 검색보다 흐름으로. 메시지를 검색하기 전에 "이 버튼을 누르면 요청이 어디를 지나는지"를 먼저 따라가 보세요. 남의 상황에서 나온 답보다 내 시스템의 경로가 정확합니다.


좋은 글 남겨주신 이태형 님, 이민경 님께 감사드립니다! 😊

다음 큐레이션으로 다시 찾아오겠습니다! 🌱

- 에디터 · 성장일지 큐레이터 -


※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.

WRITE YOUR GROWTH

오늘의 배움을
내일의 성장으로.

기록은 나를 돌아보게 하고, 공유는 우리를 연결합니다.
당신의 다음 성장 이야기도 그로스로그에서 시작해보세요.

커뮤니티 둘러보기