슬기로운 토스뱅크 개발 인턴 생활
안녕하세요, 토스뱅크 Loan Tech Platform 팀에서 백엔드 개발 인턴으로 근무하고 있는 이경민, 문홍윤입니다.
벌써 토스뱅크에 인턴으로 입사한 지 3개월이 지났습니다. 그동안 경험했던 토스뱅크의 개발 문화와 각자 진행한 프로젝트를 소개하고, 그 과정에서 느꼈던 고민과 배움들을 이 글에 정리해보려 해요.
저희는 ‘금융’이라는 다소 낯선 도메인 위에서, 빠르게 움직이는 팀의 일원으로 각자의 역할과 프로젝트를 수행하며 조금씩 성장해 나갈 수 있었습니다. 처음 접하는 기술과 환경 속에서 어려움도 있었지만, 그 모든 순간이 저희에겐 좋은 자극이 되었고, 앞으로 어떤 개발자가 되고 싶은지 진지하게 고민할 수 있는 계기가 되었어요.
그 결과, 인턴 최초로 서버 챕터 발표 무대에 설 수 있었던 특별한 경험까지 할 수 있었고, 이 과정에서 배운 것들을 기록으로 남기고자 합니다. 이 글이 토스에 관심 있는 분들이나 인턴십을 고민 중이신 분들께 조금이나마 도움이 되길 바랍니다.
Loan Tech Platform Team
저희가 속한 Loan Tech Platform Team은 대출 도메인의 시스템을 효율적인 구조로 재설계하여 개발 생산성을 올리는 팀입니다.
개발 프로젝트 소개
변수명 플러그인 by 이경민
저는 “비효율적인 요소를 발굴 및 제거합니다”라는 팀의 목표에 맞게 플러그인을 개발했습니다.
처음 은행 도메인을 접하면서 가장 어려웠던 점은 변수명을 보고도 무슨 의미인지 직관적으로 이해하기 어려운 경우가 많다는 점이었어요. 특히 도메인 특성상 복잡한 개념을 다루다 보니, 변수명은 길어지고, 사용하는 단어들도 낯설었죠.
installmentRepaymentManagementNumber
/principalRepaymentCycle
/refinancingFailReasons
같은 단어는 한눈에 파악하기 어려움
또한 새로운 변수명을 만들 때 어떤 표현이 더 자연스러운지, 또 기존에 사용된 변수명이 있는지를 확인하고 중복되거나 어색한 이름을 피하는 것이 쉽지 않았어요.
- 이자계산 같은 단어도 → /
calculateInterest
/interestCalculation
/getInterestCalculation
등 다양한 표현 중 어떤것을 사용하는지를 파악하기 어려움
플러그인 동작 흐름 요약

사용예시
IDE에서 드래그 하고 단축키를 누르기만 하면 됩니다.

입력 단어 전처리
기본적으로 DB에는 단어별로 뜻이 저장되어 있습니다. ex) 원금→principle, 상환→repayment
하지만 보통 검색하는 단어는 합성단어인 경우이기 때문에 단어를 자동으로 분리하는 기능이 필요해요.
import org.openkoreantext.processor.OpenKoreanTextProcessorJava
object VariableProcessor {
fun preprocess(word: String): String {
val spaced = when {
word.matches(Regex("^[가-힣 ]+$")) -> processKorean(word)
word.matches(Regex("^[a-zA-Z ]+$")) -> processEnglish(word)
else -> word
}
return spaced
}
private fun processKorean(word: String): String {
val normalized = OpenKoreanTextProcessorJava.normalize(word)
val tokens = OpenKoreanTextProcessorJava.tokenize(normalized)
return OpenKoreanTextProcessorJava.tokensToJavaStringList(tokens).joinToString(" ")
}
private fun processEnglish(word: String): String {
return word.split(Regex("(?=[A-Z])")).joinToString(" ").trim()
}
}
한국어 → 영어 번역
- NLP(OpenKoreanTextProcessorJava)를 사용해서 합성 단어를 단어로 분리해서 DB를 조회합니다
- ex) “원금상환주기” → “원금 상환 주기”
영어 → 한국어 번역
- 보통 변수명은 CamelCase(단어가 합쳐진 부분마다 맨 처음 글자를 대문자로 표기)로 만들어져 있기 때문에 대문자를 기준으로 단어를 분리해서 단어를 조회합니다.
- ex) “CamelCase” → “Camel Case”
LLM 사용
하지만 검색한 단어가 DB에 없을수도, 오타로 인해 원하는 단어를 검색 못할 가능성도 있습니다. 이럴 경우를 대비하여 새로운 단어의 추천, 오타 검증을 토스뱅크 내부에서 자체 구축된 LLM를 통해 지원했습니다.
새로운 단어 추천

- 검색한 단어들 중 DB에 존재하는 단어는 지원합니다.
- DB에 없는 단어는 앞뒤 문맥을 통해 LLM이 단어를 추천합니다.
앞뒤 문맥을 통한 단어 번역

이처럼 똑같은 단어를 번역해도 앞 뒤 문맥을 보고 문맥상 더 적합한 단어를 추천해요.
오타 검증

- 오타는 앞 뒤 문맥을 보고 조정 후 단어를 검색합니다
- ex) “ 상활주기” → “상환주기” → “repaymentCycle”
플러그인 도입 결과
내부 피드백
- 사전 데이터 확대
- Wisemeta에 있는 오라클 메타 단어 (~7만 개)도 등록하면 유용할 것 같다는 의견
- 매번 IDE에서 찾기 번거로우므로 사전화가 필요
- 성능 최적화 제안
- 단어 정보는 자주 바뀌지 않으므로, DB 대신 로컬 메모리 캐시를 활용하는 방식이 효율적
- 변경이 발생할 경우에는 캐시를 Evict하는 구조로 개선 가능
- 단어 수가 아주 많지 않다면, 굳이 DB 없이 파일 기반으로 관리해도 된다는 제안도 있음
- 사용성 개선 피드백
- 마우스 없이 키보드로 선택 박스 이동 가능하도록 개선하면 더 편리할 것
- LLM 추천 결과는 별도로 구분하지 않고, 바로 아래에 함께 보여주는 방식이 더 자연스러움
실제 사용 후기
- 새로운 변수명 작성이 쉬워짐
- 예: "신속채무조정" →
fastDebtAdjustment
- 자연스러운 변수명 추천이 가능해졌고, 작명 스트레스가 줄어듦
- 예: "신속채무조정" →
- 기존 코드 이해에 도움
- 익숙하지 않은 변수명도 뜻을 빠르게 파악할 수 있어, 도메인 이해 속도 향상
- 새로운 개발자가 온보딩할 때 초기 진입장벽을 낮춰줌
- 팀 내 컨벤션 관리 수단으로 활용
- Admin 페이지를 통해 단어를 손쉽게 등록·수정할 수 있음
- 이를 통해 스쿼드 단위의 변수명 컨벤션을 자율적으로 관리 가능, 실제로 몇몇 스쿼드에서 운영중
이 플러그인은 단순히 변수명을 추천하는 것을 넘어, 조직 내 변수명 컨벤션을 일관되게 유지하고, 신규 개발자의 도메인 적응 속도를 높이는 데 실질적인 도움을 줄 수 있어요. 향후에는 사용자 검색 이력을 바탕으로 한 추천 강화, 사내에서 사용되는 용어를 자동 학습하는 기능 등으로 지속적인 확장 가능성도 염두에 두고 있습니다.
SecretToString 플러그인 by 문홍윤
개발을 하다 보면, 로그나 디버깅 과정에서 객체를 문자열로 출력하는 일이 많습니다. Kotlin의 data class
는 이때 사용할 수 있는 toString()
을 자동으로 생성해주는데, 편리한만큼 잠재적인 위험이 있습니다. 예를 들어, 계좌번호나 주민등록번호 같은 민감한 정보도 그대로 노출될 수 있죠.
현재 토스뱅크는 이러한 문제에 대응해 다음 정책을 사용하고 있어요.
하지만 이 방식들은 공통적으로 개발자가 반드시 마스킹 유틸리티나 애노테이션을 올바르게 적용해야만 동작합니다. 즉, 누군가 실수로 일반 toString()
을 그대로 로그에 출력하거나, @Secret
애노테이션을 깜빡하고 빠트렸다면 민감정보가 고스란히 로그에 남게 돼요. 물론 배포 전에 검증 과정을 거치므로 실제로 이런 일이 일어나지는 않지만, 같은 코드를 반복해서 검수하고 수정하는 것은 상당히 번거로운 작업입니다.
이 문제를 해결해보고자, Kotlin 컴파일러 플러그인으로 Secret-ToString-Plugin을 만들었어요.
아이디어
애초에 @Secret
프로퍼티가 있는 data class
에 대해 미리 toString()
을 오버라이딩해서 마스킹 로직을 직접 구현해두면 이런 문제가 생기지 않습니다. 이 방식이 가장 명시적이고, IDE에서 코드도 그대로 보이기 때문에 예측가능성도 높죠.
하지만 현실적으로는 수정해야 할 data class
가 너무 많았고, 그중 일부는 공용 라이브러리나 여러 모듈에 흩어져 있었어요. 하나하나 수동으로 toString()
을 작성하는 것은 반복적이고 오류 가능성이 높은 작업이었고, 유지보수 난이도도 크게 올라갈 수밖에 없었습니다.
이 문제를 어떻게 해결할지 고민하던 중, 자연스럽게 Java 생태계에서 널리 쓰이는 https://projectlombok.org/이 떠올랐습니다.
Lombok은 toString()
이나 equals()
와 같은 반복적인 코드를 컴파일 시점에 자동으로 생성해주는 툴입니다. 덕분에 개발자는 @Data
나 @Getter/@Setter
같은 애노테이션 몇 개만 붙이면 필요한 코드를 거의 다 해결할 수 있었고, 수동으로 작성할 때 생기는 실수를 줄일 수 있어요.
결국 이 아이디어가 구체화되면서, 빌드 시점에 toString()
을 통째로 갈아끼워서 마스킹을 보장하는 컴파일러 플러그인을 만드는 쪽으로 방향이 정해졌어요.
Secret-ToString-Plugin
이 플러그인의 장점은, 적용이 굉장히 간단하다는 점입니다. 어떤 특별한 유틸리티 호출이나 별도의 복잡한 설정도 필요하지 않아요.
data class Test(
@Secret val name: String
)
val test = Test(토뱅이)
// AS-IS
println(test) // -> Test(name=토뱅이)
// TO-BE
println(test) // -> Test(name=****)
위 예제를 보시면, 플러그인을 적용했을 때와 적용하지 않았을 때의 차이를 바로 확인할 수 있습니다.
별도의 유틸리티 호출 없이, 기본 toString()
이 자동으로 마스킹된 형태로 재정의됩니다. 즉, 개발자는 그냥 평소처럼 println(test)
를 쓰면 되고, 플러그인이 알아서 안전하게 출력해 주기 때문에 실수할 여지가 거의 없어요.
작동 원리 : IR (Intermediate Representation) 조작
Kotlin 컴파일러는 소스 코드를 처리할 때 대략 다음 단계를 거칩니다:
- 파싱 / 타입체크 :
- 변수, 함수, 클래스의 타입과 구조를 해석하고 검증합니다.
- IR 생성
- 코드의 구조와 동작을 표현하는 IR 트리를 생성합니다.
- 바이트코드 생성
- JVM 바이트코드를 생성합니다.
Secret-ToString-Plugin은 이 중 IR 생성 이후 단계에서 동작합니다. Java의 Lombok은 더 이른 단계에서 코드를 삽입하지만, Kotlin은 공식 API를 통해 이미 타입과 심볼이 확정된 IR을 안전하게 수정할 수 있어요. 실무적으로 모든 프로퍼티와 함수 등을 직접 참조할 수 있어 구현 및 로직이 단순해지고, 다른 빌드 파이프라인과 충돌하지 않는 장점이 있습니다.
Secret-ToString-Plugin의 동작 흐름은 다음과 같습니다.
1️⃣ IR 트리 순회
- 모든
data class
를 탐색합니다.

2️⃣ 애노테이션 탐색
- 각 프로퍼티에
@Secret
어노테이션이 붙어있는지 검사합니다. - 마스킹 대상 클래스라면 처리 목록에 추가합니다.

3️⃣ 기존 toString()
제거
- 컴파일러가 자동 생성한 기본
toString()
함수를 찾아 삭제합니다.

4️⃣ 새로운 toString()
생성
- 먼저
toString()
의 시그니처들을 정의합니다.- 다른 클래스에서 이 함수를 참조할 때 필요한 일종의 “포인터”입니다.

- 다음으로 바디를 생성해 실제 출력 로직을 작성합니다.
- 실제 어떤 동작을 하는지 적어놓은 “구현 내용”입니다.
@Secret
프로퍼티는 항상 마스킹 로직을 거쳐 출력합니다.- 나머지 프로퍼티는 본래 값을 출력하거나, 객체일 경우 그 객체의
toString()
시그니쳐를 참조해 호출합니다.

- 바디를 만들 때 참조 대상에
toString()
시그니처가 존재하지 않으면 컴파일 과정에서의 NPE가 발생하기 때문에, 먼저 모든 시그니처를 생성해둔 뒤 바디를 만드는 순서로 구현했습니다.

실제 환경에서는?
처음에는 “모든 data class
에 전면 적용해버리면, 유틸리티를 깜빡하는 문제가 원천 차단되겠다”는 아이디어로 시작했습니다.
그러나 팀 내에서 논의해 본 결과, 몇 가지 중요한 우려가 제기되었어요:
- 코드에 표시되지 않는 자동 동작이 지나치게 많으면 추적성이 떨어진다.
- 새로운 개발자나 다른 팀에서는 플러그인의 존재를 모르고 예상치 못한 출력 때문에 디버깅이 어려워질 수 있다.
결국 이런 리스크를 고려해 보수적인 접근 방식을 채택했습니다:
@SecretToString
애노테이션이 달린 클래스에만 기능 적용- 적용하는 레포지토리와 팀의 판단 하에 옵션을 주어 전체
data class
에 일괄 적용 가능 - 이 경우
@DisableSecretToString
으로 일부 클래스만 예외 처리
이렇게 해서 필요한 범위에만 명확한 의도를 표시해 안전망을 만들 수 있고, 코드에 의도가 드러나기 때문에 협업에서 혼란을 줄일 수 있었습니다.
도입 결과
이 프로젝트로 인해, SecretUtils.toString()
을 누락하는 것은 어느 정도 해결했으나, 애초에 @Secret
을 선언 시 빠트리는 문제는 완전히 해결하지 못했어요. 분량 상 이 글에서 설명은 못했지만, 이를 개발 단계에서 패턴 매칭 등을 이용해 자동으로 감지하는 별개의 프로젝트도 진행해 여신 도메인에 적용했습니다.
무엇보다 이 두 개선은 보안을 강화하는 데 그치지 않고, 민감 정보 처리에 대한 개발자의 부담을 근본적으로 줄이는 계기가 되었어요. 짧은 기간이었지만, 실제 프로덕션 환경에 적용되어 팀 전체의 개발 프로세스를 한층 더 견고하고 효율적으로 만드는 데 기여할 수 있었던 점이 큰 보람으로 남습니다.
토스 인턴 분들이 궁금해요

Q. 지금은 어떤 일을 하고 계신가요?
경민: 저는 Loan Tech Platform에서의 일도 정말 즐거웠지만, 토스뱅크에 온 만큼 은행 도메인 자체를 깊이 경험해보고 싶었어요. 고민 끝에 팀 이동을 요청드렸고, 흔쾌히 승인해 주신 덕분에 지금은 CPL(기업부동산스쿼드)에서 활동하고 있어요. CPL은 은행 도메인 중에서도 특히 복잡한 영역이라 처음 이해하는 데 어려움을 겪었지만, 팀원 분들의 적극적인 도움 덕분에 빠르게 적응할 수 있었어요. 지금은 LTI, RTI 같은 대출 규제 관련 로직을 직접 담당하며 개발에 참여하고 있어요.
홍윤: 저는 Loan Tech Platform에서 하는 일이, 작은 개선 하나에도 여러 팀에 파급력이 미친다는 점이 늘 걱정되어 조심스러워지면서도 동시에 흥미로웠어요. 그래서 경민님과는 다르게 계속 이 팀에 남아 더 다양한 작업을 하기로 했어요. 그 이후에는 여신 도메인에서 공통으로 사용하는 다양한 설정값을 한 곳에서 관리할 수 있게 하는 서버를 새롭게 만들어 배포하고, 여기에 연계되어 실시간으로 바뀔 수 있는 설정값들을 접근할 수 있는 라이브러리를 개발했어요. 그 과정에서 토스뱅크에서 사용하는 다양한 기술들을 익히며, 효율적이면서 장애상황 등에서도 안정적으로 동작할 수 있는 로직을 구현하는 등, 다양한 프로젝트에 참여하고 있어요.
Q. 토스에서의 생활이 궁금해요!
경민: 토스에는 DRI(Directly Responsible Individual)라는 문화가 있어요. 최소한의 일만 정해주고, 나머지는 스스로 문제를 정의하고 해결하는 구조인데요, 덕분에 인턴임에도 여러 프로젝트를 직접 리드하며 경험해볼 수 있었어요. 처음에는 최종 의사결정까지 책임져야 한다는 점이 부담스럽게 느껴졌지만, 팀원들과 초안을 공유하고 피드백을 주고받는 과정을 거치며 더 나은 방향을 함께 고민할 수 있었어요. 그렇게 점차 책임감을 갖고 주도적으로 일하는 방식에 익숙해졌고, 제가 원하는 방향으로 문제를 해결해나가며 빠르게 성장하고 있다고 느끼고 있어요.
홍윤: 저는 토스의 가장 큰 장점은 수평적인 개발 문화에 있다고 생각해요. 단순히 남이 시키는 일을 하라는 대로 하는게 아니라, 인턴임에도 의견을 제안하거나 로직을 개발할 때 다른 선배 개발자분들과 토의하는 일이 많았어요. 그 과정에서 제가 생각해보지 못했던 시각이나 더 나은 방법을 자주 접하게 되었는데, 덕분에 이전보다 더 주도적으로 새로운 기술을 시도하고 학습하거나, 익숙하지 않은 영역도 빠르게 익히려는 태도가 자연스럽게 생긴 것 같아요.이런 환경에서 인턴을 하다 보니, 단순히 주어진 일을 처리하는 것을 넘어 어떻게 하면 더 좋은 방향으로 문제를 풀 수 있을지 고민하고, 직접 제안하고 실행하는 경험을 많이 쌓는 것 같아요


Q. 토스에서 좋았던 점은 무엇인가요?
경민: 토스에서의 생활은 개발자로 성장하기에 정말 최적의 환경이라고 느꼈어요. 개발 환경 세팅부터 온보딩 시스템까지 체계적으로 잘 마련되어 있어서, 처음 시작할 때도 큰 어려움 없이 적응할 수 있었어요. 필요하다면 서적이나 교육 자료도 자유롭게 활용할 수 있었고, 주변에 계신 뛰어난 팀원분들과의 커뮤니케이션을 통해 실무적인 경험도 자연스럽게 얻을 수 있었어요. 또한, 사내 커피숍이나 편의점, 휴게 공간 같은 복지 시설 덕분에 개발에만 온전히 집중할 수 있는 환경이 만들어졌다고 느꼈어요.
홍윤: 저는 이론적인 부분만 공부해왔어서, 개발 경험이 거의 없던 상태로 인턴으로 입사했어요. 그래서 적응을 못할까봐 걱정이 꽤 많았었는데, 입사하고 보니 의미없는 고민이었던 것 같아요. 신규 입사자를 위해서 토스뱅크의 전체적인 환경 설정, 구조, 사용하는 기술 등이 너무 체계적으로 잘 정리되어 있었고 , 무엇보다 메이트분들이 적극적으로 이것저것 알려주려 하셔서 적응에 어려움이 거의 없었어요. 덕분에 토스의 환경에 빨리 익숙해졌고, 온전히 저의 역량을 키우고 프로젝트에 집중할 수 있었어요.
Q. 마지막으로 하고 싶은 말
경민: 많이 부족하고 서툴렀던 첫 인턴이었지만, 토스에서의 경험은 제가 어떤 개발자로 성장해나가야 할지 방향을 명확하게 잡을 수 있었던 계기였어요. 작은 기능 하나를 구현할 때도 단순히 동작하게 만드는 것을 넘어서, “왜 이 기능이 필요한가?”, “더 나은 방식은 없을까?” 라는 질문을 끊임없이 던지게 되었고, 이런 고민은 곧 기술적인 해결 능력은 물론, 제품 관점에서 사고하는 습관으로 이어졌어요. 토스에서의 인턴십은 단순히 코드를 작성해보는 경험을 넘어서,문제를 주도적으로 정의하고 해결하는 개발자로 성장할 수 있도록 만들어준 정말 소중한 기회였어요.
홍윤: 책으로 기술을 배우는 것만으로는 채울 수 없는 배움으로 가득찼던 시간이었어요. 처음에는 어떻게든 주어진 기능을 실수 없이 구현하는 것에만 집중했는데, 점점 더 “왜 이 방식이어야 하는지”, “이게 정말 좋은 방향인지”를 스스로 물어보게 됐던 것 같아요. 그 과정에서 단순히 잘 동작하는 코드를 넘어서, 직관적인 논리 흐름과 확장 가능성 등 더 다양한 요소를 고려하는 개발을 하게 됐어요. 무엇보다도 인상적이었던 건, 누구나 아이디어를 제안할 수 있고 그 아이디어를 자유롭게 구현해볼 수 있었던 문화 덕분에 스스로 책임감을 가지고 일을 끝까지 밀어붙이는 경험을 할 수 있었어요. 당연히 아직 부족한 점이 많지만, 앞으로도 이렇게 문제를 주도적으로 정의하고 더 나은 해답을 찾으려는 태도를 계속 가져가고 싶어요.