
동료 개발자들에게서 배운 코드들
회사에서 협업하며 만난 동료 개발자들의 코드에서 배운 점들을 정리합니다. 실용적인 코드 스타일, 설계 방식, 디버깅 팁 등 실제 업무에서 유용했던 내용을 담았습니다.
시작하며
회사에서 코드 리뷰를 하며 동료 개발자들의 코드를 읽다 보면, 때때로 "아, 이렇게도 할 수 있구나!" 하는 순간들이 있습니다. 오늘은 그동안 팀원들의 코드에서 발견한 유용한 JavaScript 패턴들을 정리해보려고 합니다.
어떤 건 사소한 부분이지만, 실제로 적용해보면 코드의 품질이나 유지보수성에 큰 차이를 만들 수 있었습니다.
let 을 제거하는 효율적인 방법 - 즉시 실행 함수 활용
평소 객체에 조건부로 값을 추가해야 할 때마다 습관처럼 let을 사용하곤 했습니다. 더 나은 방식이 있을 것 같다는 막연한 생각은 있었지만, 뾰족한 대안이 떠오르지 않아 늘 비슷한 방식으로 처리해왔죠.
그러다 동료의 코드에서 let 없이도 깔끔하게 작성된 패턴을 보게 되었습니다. **즉시 실행 함수(IIFE)**를 활용해 조건에 따라 객체에 값을 추가하는 방식이었죠.
즉시 실행 함수 자체는 알고 있었지만, 실무에서 이렇게 활용할 수 있다는 건 처음 알게 되었습니다. 단순히 문법 지식으로만 알고 있던 개념이, 실전 코드에서 얼마나 유용하게 쓰일 수 있는지를 느낀 순간이었습니다.
// let을 사용한 일반적인 방식
let payload = {
page,
pageSize,
}
if (query) {
payload.q = query
}
if (sortBy) {
payload.sort = sortBy
}
if (isAdmin) {
payload.role = "admin"
}
if (isActive) {
payload.status = "active"
}
let을 사용하면 변수가 나중에 재할당될 수 있다는 암시를 주게 됩니다. 코드를 읽는 사람은 이 변수가 다른 곳에서 또 변경되는지 계속 신경 써야 합니다.
// 동료에게 배운 IIFE 패턴
const payload = (() => {
const base = {
page,
pageSize,
}
if (query) {
base.q = query
}
if (sortBy) {
base.sort = sortBy
}
if (isAdmin) {
base.role = "admin"
}
if (isActive) {
base.status = "active"
}
return base
})()
이렇게 작성하면 payload는 const로 선언되며, 모든 로직은 즉시 실행 함수(IIFE) 내부에 캡슐화됩니다. 이를 통해 코드의 불변성, 스코프의 명확한 분리, 의도의 명확화라는 세 가지 이점을 얻을 수 있습니다.
먼저, const를 사용함으로써 재할당을 방지하고, 해당 변수는 한 번만 계산되어 사용된다는 점을 명확히 할 수 있습니다.
또한, 조건별 로직이 외부 스코프를 오염시키지 않고 함수 내부에 격리되기 때문에 코드가 더 안전하고 예측 가능해집니다.
마지막으로, 이 값은 어떤 조건에 따라 초기화된 "최종 결과값"이라는 의도를 코드 자체로 표현할 수 있습니다.
이러한 특성들 덕분에 코드의 가독성과 유지보수성이 크게 향상됩니다.
하위 호환성을 쉽게 대응하는 방법 - API 버저닝
지금은 당연하게 느껴지지만, 현재 회사에 오기 전까지는 API 버전 관리의 중요성을 제대로 인식하지 못했습니다.
// 기존 방식
const API_ENDPOINTS = {
users: "/api/users",
posts: "/api/posts",
comments: "/api/comments",
}
// 문제: API 변경 시 모든 클라이언트가 영향을 받음
// - 앱 업데이트를 하지 않은 사용자는 서비스 이용 불가
// - 점진적 마이그레이션 불가능
// - 긴급 롤백 시 모든 버전이 영향받음
이전에는 크게 문제가 되지 않았지만, 현재 회사처럼 대규모 트래픽이 발생하고, 사용자들의 앱 업데이트 시점이 제각각인 환경에서는 이러한 구조에 여러 한계가 드러납니다.
지금 회사에서는 아래처럼 명시적인 버전이 포함된 API 구조를 사용하고 있습니다.
// 현재 회사의 버전 관리 방식
const API_ENDPOINTS = {
v1: {
users: "/api/v1/users", // 2023년 출시 버전
posts: "/api/v1/posts",
},
v2: {
users: "/api/v2/users", // 2024년 개선 버전
posts: "/api/v2/posts",
reactions: "/api/v2/reactions", // v2에서 새로 추가
},
}
이런 방식이 주는 장점은 명확합니다.
-
하위 호환성 보장 새로운 기능이나 데이터 구조를 도입하더라도, 기존 앱은 여전히 v1 API를 통해 안정적으로 동작할 수 있습니다. 덕분에 "업데이트를 안 한 사용자"도 서비스에 영향을 받지 않습니다.
-
점진적인 이전 가능 한 번에 모든 클라이언트를 바꾸지 않아도 되기 때문에, v1 → v2로 점진적으로 이전하는 전략이 가능해집니다. 이는 제품 운영 측면에서 큰 안정성을 제공합니다.
-
리팩터링/설계 변경의 자유도 증가 기존 API를 유지한 채, 새로운 버전에서 설계를 완전히 바꾸는 것도 가능하기 때문에, 서버 개발자가 더 유연하게 설계를 개선할 수 있습니다.
Regex match 함수 활용 - 간결한 패턴 매칭
실무에서 정규식을 사용할 때, 주로 RegExp.prototype.test()
함수를 활용해 문자열이 특정 패턴과 일치하는지 여부만 간단히 확인하곤 했습니다.
test()
함수는 정규식 객체에 내장된 메서드로, 입력 문자열이 패턴에 맞으면 true, 그렇지 않으면 false를 반환합니다.
즉, 패턴 매칭 여부만 판별할 때 간편하게 사용할 수 있는 불리언 반환 함수입니다.
예를 들어 다음과 같은 URL이 있다고 가정해봅시다.
/users/123/posts/456
이 URL에서 userId = 123, postId = 456 값을 추출하고 싶다면 아래와 같이 test()
함수를 활용할 수 있습니다.
const path = "/users/123/posts/456"
const isPostDetail = /^\/users\/\d+\/posts\/\d+$/.test(path)
console.log(isPostDetail) // true
// 하지만 userId, postId는 알 수 없음
test()
는 "패턴에 맞는지 여부"만 알려주기 때문에, 값을 추출하려면 한 번 더 작업이 필요합니다.
하지만 String.prototype.match()
를 활용하면, 정규식과 일치한 값들을 배열 형태로 받아 활용할 수 있습니다.
const path = "/users/123/posts/456"
const matched = path.match(/^\/users\/(\d+)\/posts\/(\d+)$/)
if (matched) {
const userId = matched[1]
const postId = matched[2]
console.log("User ID:", userId) // 123
console.log("Post ID:", postId) // 456
}
match() 함수의 반환값 설명
- matched[0]: 정규식 전체와 일치하는 문자열 ("/users/123/posts/456")
- matched[1]: 첫 번째 캡처 그룹에 해당하는 값 ("123")
- matched[2]: 두 번째 캡처 그룹에 해당하는 값 ("456")
- matched.index: 일치하는 문자열의 시작 위치 (여기서는 0)
- matched.input: 원본 문자열 ("/users/123/posts/456")
- matched.groups: 명명된 캡처 그룹이 있을 경우 포함 (없으면 undefined)
이처럼 test()는 단순히 일치 여부 확인에 적합하고, match()는 일치한 값을 추출하거나 캡처 그룹을 활용할 때 유용합니다.
마치며
동료들의 코드를 읽는 것은 정말 좋은 학습 방법인 것 같습니다. 책이나 강의에서는 배울 수 없는, 실무에서 부딪히며 체득한 노하우들이 코드 한 줄 한 줄에 녹아있기 때문입니다.
같은 문제를 다른 관점에서 해결하는 방법을 보면서, 내 코드도 한 단계 성장할 수 있었습니다.
가장 인상 깊었던 점은, 이런 패턴들이 거창한 기술이 아니라는 것입니다. 즉시실행 함수도, match()
메서드도 모두 JavaScript의 기본 기능들입니다. 다만 이것들을 언제, 어떻게 활용하느냐가 차이를 만들어 낸 것 같습니다.
가끔은 리뷰 과정이 지루하고 피곤하게 느껴지기도 하지만, 이 글을 쓰면서 다시 한번 더 신경 써서 임해야겠다는 다짐을 하게 되었습니다.