안녕하세요! 그로스로그 입니다!😊 🌱
쿼리를 쓰지 않았는데 UPDATE가 나가고, 메서드 이름만 지으면 구현체가 생깁니다. 편해진 만큼 안 보이게 된 것들에 대한 세 편입니다.
성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 이번 글은 1기를 다시 읽는 네 번째 큐레이션입니다.
4회차는 2024년 7월 13일이었습니다. 이 회차에 우연히 "프레임워크에 무언가를 맡기는 이야기" 세 편이 나란히 올라왔어요.
01
🗄️ JPA에서 중요한 역할을 하는 영속성 컨텍스트 소개
이범수 님
JPA를 쓰면 개발자가 SQL을 거의 쓰지 않게 되는데, 그 일을 대신 해주는 것이 영속성 컨텍스트입니다. 이 글은 그 안에서 실제로 무슨 일이 벌어지는지를 세 가지로 나눠 설명합니다.
1차 캐시 — 같은 데이터를 두 번 조회하면 두 번째는 DB에 가지 않습니다.
쓰기 지연 저장소 —
persist를 호출해도 INSERT가 바로 나가지 않고 커밋 시점에 몰아서 나갑니다.변경 감지(Dirty Checking) — 값만 바꿔두면 UPDATE 쿼리를 JPA가 알아서 만들어 보냅니다.
JPA를 처음 쓰면 가장 이상한 게 이겁니다. save()를 안 불렀는데 데이터가 바뀌어 있는 것.
객체의 필드값만 바꿨을 뿐인데 트랜잭션이 끝나면 데이터베이스에 반영돼 있습니다. 마법 같아 보이는데, 그 마법의 이름이 영속성 컨텍스트예요.
조금 더 들어가 보면
영속성 컨텍스트는 애플리케이션과 데이터베이스 사이에 있는 중간 보관소입니다. 글에서는 "엔티티를 효율적으로 쉽게 관리하기 위해 만들어진 공간"이라고 표현했어요.
세 가지 기능이 각각 다른 문제를 풉니다.
🗃️ 1차 캐시 — 같은 것을 두 번 묻지 않는다
1차 캐시는 조회 횟수를 줄입니다. em.find(Memo.class, 1)을 부르면 이렇게 갈립니다.
2. 캐시 저장소에 값이 없을 때: DB에 SELECT 조회 후 캐시 저장소에 값을 저장하고 반환
같은 트랜잭션 안에서 같은 데이터를 열 번 조회해도 DB에는 한 번만 갑니다.
여기에 부수 효과가 하나 더 있습니다. 같은 데이터를 여러 번 읽으면 항상 같은 객체가 돌아옵니다. 캐시에 있는 걸 그대로 주니까요. 그래서 a == b가 참이 됩니다. 서로 다른 곳에서 같은 회원을 조회해도 하나의 객체를 공유하게 되는 거예요.
이게 왜 중요하냐면, 한쪽에서 값을 바꾸면 다른 쪽에도 즉시 반영되기 때문입니다. 같은 객체니까요. 만약 조회할 때마다 새 객체를 만들어 준다면, 어느 것을 바꿔야 저장되는지 알 수 없게 됩니다.
글이 짚은 캐시 저장소의 자료구조가 이걸 뒷받침합니다.
키가 식별자(ID)라는 게 핵심입니다. 그래서 ID로 찾을 때만 1차 캐시가 동작하고, 조건으로 검색하는 JPQL은 캐시를 거치지 않고 DB로 갑니다. 이걸 알면 "왜 어떨 때는 쿼리가 나가고 어떨 때는 안 나가지?"가 풀립니다.
📮 쓰기 지연 — 모아서 한 번에 보낸다
쓰기 지연은 데이터베이스에 가는 횟수를 줄입니다. 저장 요청이 올 때마다 바로 보내지 않고 쌓아뒀다가 커밋 시점에 한꺼번에 보내요.
이게 왜 이득인지는 데이터베이스가 다른 컴퓨터에 있다는 걸 떠올리면 분명해집니다. 쿼리 하나를 보내고 답을 받는 데는 네트워크를 한 번 왕복하는 시간이 듭니다. 계산 자체는 순식간이어도 오가는 시간은 그대로예요. 열 건을 따로 보내면 열 번 왕복하고, 모아서 보내면 한 번입니다.
그리고 여기에 안전장치의 성격도 있습니다. 아직 안 보냈으니, 중간에 문제가 생기면 그냥 버리면 되거든요. 이미 보낸 것을 되돌리는 것보다 훨씬 간단합니다.
🔍 변경 감지 — 처음 상태를 복사해둔다
그리고 변경 감지가 가장 흥미롭습니다. 글의 설명을 보면 동작이 명확해집니다.
트랜잭션 커밋 및 flush() 호출 : (…) 쓰기 지연 저장소에 있는 엔티티들의 현재 상태와 최초 상태를 비교합니다.
더티 체킹 : (…) JPA는 엔티티의 모든 필드를 비교하여 변경된 필드가 있는 경우에만 해당 필드를 Update SQL에 포함시킵니다.
처음 읽어온 상태를 따로 복사해두고, 나중에 비교해서 달라진 것만 UPDATE에 넣는 방식입니다. 그래서 개발자가 "이 필드를 바꿨으니 UPDATE를 날려라"라고 말할 필요가 없어요.
그런데 여기서 대가가 하나 생깁니다. 쿼리가 언제 나가는지 코드에 안 보입니다. member.setName("홍길동") 이 줄에서는 아무 일도 일어나지 않고, 한참 뒤 트랜잭션이 끝나는 시점에 UPDATE가 나갑니다. 코드를 위에서 아래로 읽어서는 그 순간을 짚을 수 없어요.
JPA로 만든 서비스가 느려졌을 때 원인 찾기가 어려운 이유가 여기 있습니다. 쿼리를 내가 쓰지 않았으니, 어떤 쿼리가 몇 번 나가는지도 내가 모르거든요.
그리고 이 편리함에는 보이지 않는 비용이 하나 더 있습니다. 처음 상태와 비교하려면 그 처음 상태를 어딘가에 들고 있어야 하거든요.
즉, 엔티티를 하나 읽을 때마다 JPA는 그 값의 복사본을 따로 보관합니다. 한두 건이면 아무 문제가 없지만, 조회 결과가 수만 건이면 그 복사본도 수만 개가 됩니다. 메모리를 두 배로 쓰는 셈이에요.
그래서 대량 데이터를 읽기만 할 때는 영속성 컨텍스트에 담지 않는 방식을 씁니다. 어차피 고칠 게 아니라면 변경 감지가 필요 없으니까요. 통계 집계나 목록 내려주기 같은 작업에서 JPA 대신 쿼리를 직접 쓰는 경우가 있는데, 그 이유 중 하나가 이겁니다.
편의 기능은 대부분 어딘가에 값을 더 들고 있는 방식으로 구현됩니다. 그게 캐시든 스냅샷이든요. 무엇을 더 들고 있는지 알면 언제 그 기능이 부담이 되는지도 보입니다.
📝 원문에서는 이렇게 짚습니다
flush()에 대한 한 줄 정의가 정확합니다.
flush()를 "저장하는 것"으로 오해하는 경우가 많은데, 쌓여 있던 SQL을 지금 내보내는 것이지 커밋이 아닙니다. 그래서 flush() 후에도 롤백하면 없던 일이 됩니다. 이 구분을 못 하면 테스트 코드에서 헤매게 돼요.
두 개의 관리자를 구분해둔 것도 좋았습니다.
엔티티 매니저 — 영속성 컨텍스트에 접근하여 엔티티 객체들을 조작할 수 있다.
팩토리는 하나, 매니저는 여럿입니다. 그리고 이 관계가 실제 동작을 결정해요.
엔티티 매니저 팩토리는 애플리케이션이 뜰 때 딱 하나 만들어집니다. 데이터베이스 접속 정보를 읽고, 엔티티 클래스들을 스캔하고, 커넥션 풀을 준비하는 무거운 작업이라 여러 번 할 수 없거든요.
반면 엔티티 매니저는 요청마다 새로 만들어집니다. 사용자 A가 주문을 넣는 작업과 사용자 B가 상품을 조회하는 작업은 서로 섞이면 안 되니까요. 각자 자기 영속성 컨텍스트를 갖고, 작업이 끝나면 버립니다.
그래서 영속성 컨텍스트의 수명은 대체로 트랜잭션 하나입니다. 1차 캐시가 "같은 트랜잭션 안에서만" 유효한 이유가 이거예요. 작업이 끝나면 매니저와 함께 캐시도 사라집니다.
이걸 모르면 "캐시가 있다니 두 번째 요청부터는 빠르겠네"라고 오해하기 쉽습니다. 1차 캐시는 요청 사이에 남아 있는 캐시가 아니라, 한 작업 안에서 같은 것을 두 번 묻지 않게 하는 장치입니다.
큐레이터 노트
1,804자짜리 짧은 글인데 구조가 정확합니다. 영속성 컨텍스트라는 하나의 개념을 세 기능으로 쪼개고, 각 기능이 어떤 순서로 동작하는지를 단계별로 적었어요.
이범수 님은 1기 내내 이런 글을 씁니다. Stream API, Docker MySQL 복제, Spring AOP, MSA, 낙관적 락. 한 회차에 하나씩, 개념 하나를 골라 자기 언어로 정리해요. 분량은 대개 2천 자 안팎입니다.
짧게 쓰는 게 대충 쓰는 것과 다르다는 걸 보여주는 글입니다. 곁가지를 치고 핵심만 남기려면 오히려 더 정확히 알아야 하거든요.
5회차와 7회차 큐레이션에서 다시 만나실 겁니다.
02
🍃 Spring Boot Data JPA
형기상 님
11,361자짜리 긴 필기입니다. 「실전! 스프링 데이터 JPA」 강의를 들으며 정리한 글이에요.
순수 JPA로 리포지토리를 만드는 방법부터 시작해서, Spring Data JPA가 그걸 어떻게 대신해주는지로 넘어갑니다. 인터페이스만 선언하면 구현체가 자동으로 만들어지는 지점이 핵심이에요.
그리고 쿼리를 만드는 네 가지 방법(메서드 이름·NamedQuery·
@Query·DTO 조회)을 각각 코드와 함께 비교하고, 실무에서 무엇을 쓰고 무엇을 안 쓰는지까지 적었습니다.2회차 큐레이션에서 형기상 님의 「Spring Boot 강의 필기」를 소개해드렸습니다. 그로부터 한 달, 분량이 7,729자에서 11,361자로 늘었습니다.
조금 더 들어가 보면
Spring Data JPA가 하는 일을 한 문장으로 줄이면 이렇습니다. 구현체를 대신 만들어준다.
글에 적힌 설명이 정확합니다.
빌드 시 Spring Data JPA가 JpaRepository를 보고서 스캔하여 구현체 생성
(개발자가 구현체를 만들지 않아도 알아서 만듦)
인터페이스는 "이런 기능이 있다"는 선언일 뿐이고, 원래는 그걸 실제로 구현한 클래스가 있어야 합니다. Spring Data JPA는 그 클래스를 애플리케이션이 뜰 때 자동으로 만들어냅니다. 그래서 개발자는 선언만 하고 끝입니다.
여기서 더 나가면 메서드 이름으로 쿼리를 만드는 기능이 나옵니다. 글이 두 방식을 나란히 놓고 비교해요.
순수 JPA로 쓰면 이렇습니다.
public List<Member> findByUsernameAndAgeGreaterThen(String username, int age) {
return em.createQuery("select m from Member m where m.username = :username and m.age > :age")
.setParameter("username", username)
.setParameter("age", age)
.getResultList();
}
Spring Data JPA로는 이 한 줄입니다.
List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
메서드 이름을 파싱해서 쿼리를 만듭니다. findBy + Username + And + Age + GreaterThan을 읽어서 WHERE 절을 조립하는 거예요.
그런데 글은 여기서 멈추지 않고 한계선을 함께 적었습니다.
- 파라미터 1~2개까지를 사용해야 적당
이 두 줄이 중요합니다. 필드 이름을 바꾸면 메서드 이름도 같이 바꿔야 하고, 조건이 서너 개만 돼도 메서드 이름이 문장처럼 길어집니다. findByUsernameAndAgeGreaterThanAndTeamNameAndStatusOrderByCreatedAtDesc 같은 게 나오면 편한 게 아니라 읽기 어려워져요.
그래서 실무에서는 조건이 많아지면 @Query로 직접 쓰거나 Querydsl로 넘어갑니다. 형기상 님은 다음 회차에 정확히 Querydsl로 갑니다.
🧪 JPQL은 무엇을 대상으로 하는가
필기 중간에 한 줄로 지나가는 정의가 있는데, 사실 JPA를 이해하는 핵심입니다.
Entity 대신에 테이블을 조회 할 때, SQL로 번역 후 실제 DB에서 데이터를 가져옴
테이블이 아니라 객체에 질의한다는 뜻입니다.
SQL은 select * from member m where m.username = ?처럼 테이블 이름과 컬럼 이름을 씁니다. JPQL은 select m from Member m where m.username = ?으로 거의 같아 보이지만, 여기서 Member는 테이블이 아니라 자바 클래스고 username은 컬럼이 아니라 필드예요.
이 차이가 실제로 어떤 이득을 주냐면, 데이터베이스를 바꿔도 쿼리를 안 고쳐도 됩니다. JPQL을 실제 SQL로 번역하는 건 하이버네이트가 하는데, 어느 데이터베이스를 쓰는지에 맞춰 번역해주거든요. 2회차 필기에 나왔던 dialect 설정이 그 번역 규칙을 정하는 항목입니다.
그리고 오타를 더 일찍 잡을 수 있습니다. 테이블 이름을 잘못 쓰면 실행해봐야 알지만, 클래스와 필드 이름은 애플리케이션이 뜰 때 검사할 수 있으니까요.
대신 대가도 있습니다. 번역된 SQL이 내가 예상한 것과 다를 수 있어요. 그래서 2회차에서 본 org.hibernate.SQL: debug 로그를 켜두는 게 중요합니다. 무엇을 쓰든 결국 나가는 건 SQL이니까요.
필기가 "JPA 기술 선행이 필수"라고 못 박아둔 것도 같은 맥락입니다.
JPA 기술 선행이 필수
편의 계층만 배우면 문제가 생겼을 때 내려갈 수가 없습니다. 메서드 이름으로 쿼리가 만들어지는 마법을 쓰려면, 그 아래에서 JPQL이 만들어지고 다시 SQL로 번역된다는 걸 알아야 해요.
📝 원문에서는 이렇게 판단합니다
이 필기가 단순 요약이 아닌 이유는, 기능마다 쓸지 말지에 대한 판단이 붙어 있어서입니다.
NamedQuery에 대해서는 이렇게 적었어요.
- 이 기능보다 Repository Method에 바로 Query를 지정할 수 있는 기능이 매우 막강
그런데 그냥 버리지 않고 장점 하나는 남겨둡니다.
이게 왜 값진 정보냐면, 문자열로 쓴 쿼리는 보통 실행해봐야 오류를 안다는 문제가 있기 때문입니다. 오타 하나가 배포 후에 터지죠. NamedQuery는 앱이 뜰 때 검사해서 미리 막아줍니다.
그리고 실무에서 자주 쓴다는 @Query에도 같은 장점이 있다고 이어서 적어뒀습니다.
- 애플리케이션 로딩할 때 오타 발견시 오류 바로 출력
- 동적 쿼리는 어떻게?
마지막의 "동적 쿼리는 어떻게?" — 자기가 아직 모르는 것을 물음표로 남겨둔 겁니다. 그리고 다음 편에서 그 답(Querydsl)으로 갑니다.
큐레이터 노트
11,361자짜리 필기를 큐레이션에 넣은 이유는 정리의 밀도 때문입니다.
강의 필기는 보통 "이렇게 하면 된다"만 남습니다. 이 글에는 그 위에 세 가지가 더 있어요. 실무에서 쓰는지 안 쓰는지, 왜 그런지, 그리고 아직 모르는 것입니다.
특히 세 번째. "동적 쿼리는 어떻게?"처럼 자기가 못 푼 질문을 필기에 남겨두는 사람은 많지 않습니다. 대개는 아는 것만 적고 모르는 건 지웁니다. 그런데 물음표를 남겨두면 다음에 무엇을 공부할지가 정해집니다.
형기상 님의 1기 다섯 편은 이 방식으로 이어집니다. 강의 필기 → 프로젝트 필기 → Data JPA → Querydsl → 그리고 마지막에 자기 프로젝트의 서버 구성도. 물음표가 다음 편의 제목이 되는 구조예요.
6회차 큐레이션에서 그 마지막 편을 소개해드리겠습니다.
03
🔄 Redux (2) React 상태 관리
이지은 님
3회차에 소개해드린 「Redux (1)」의 다음 편입니다. 지난 편이 밤을 샌 이야기였다면, 이번 편은 그때 골랐어야 할 것을 다시 검토하는 글이에요.
Context API, Recoil, Redux 세 가지를 나란히 놓고 같은 틀로 비교합니다. 상태를 관리하는 주체는 무엇인지, 상태값은 어떻게 바뀌는지, 컴포넌트는 그걸 어떻게 받아 가는지.
그리고 각 도구가 어떤 프로젝트에 맞는지까지 적었습니다.
지난 편의 마지막 문장이 "다음 편에는.. 이론과..실전을.."이었습니다. 그 다음 편이 이 글입니다.
그런데 시작이 좀 아픕니다.
하루 만에 정해야 했다는 게 이 글 전체의 배경입니다. 그래서 이 글은 뒤늦게 하는 조사예요.
조금 더 들어가 보면
React에서 상태 관리 도구가 필요한 이유는 props drilling 때문입니다. 화면 맨 위의 정보를 맨 아래 조각에 전달하려면, 그 사이에 있는 모든 조각을 거쳐야 해요. 중간 조각들은 그 값이 필요 없는데도 받아서 넘겨주기만 합니다. 층이 깊어질수록 지저분해지죠.
글은 세 가지 해법을 같은 틀로 정리했습니다.
Context API는 React가 기본으로 주는 기능입니다. 값을 위에 매달아두면 아래 조각들이 직접 꺼내 씁니다. 별도 설치가 필요 없어요. 대신 이런 대가가 있습니다.
값 하나가 바뀌면 그 아래 전부가 다시 그려집니다. 조각이 몇 개 없을 땐 문제가 없지만 많아지면 화면이 버벅여요. 그래서 글은 "상태값의 유형이 단순하고 상태 변화가 적은 소규모의 프로젝트에 적합하다"고 정리합니다.
Recoil은 상태를 잘게 쪼개서 관리합니다. atom 하나가 값 하나예요. 필요한 조각만 그 atom을 구독하니 관계없는 곳은 다시 그려지지 않습니다. 설정도 간단합니다. 대신 "메모리 누수나 내부적인 오류, 캐싱 등의 문제가 지적되고 있다"고 짚었어요.
Redux는 가장 엄격합니다. 글의 설명이 좋습니다.
"사전에 정의한 뒤"가 핵심입니다. 무엇을 바꿀 수 있는지(action), 어떻게 바뀌는지(reducer)를 미리 다 정해둬야 합니다. 그래서 설정이 많고, 그래서 초보에게 "그렇게까지?"라는 말이 나왔던 거예요.
그런데 3회차 글을 떠올려보면, 이 팀이 겪은 문제가 정확히 "미리 정하지 않아서" 생긴 것이었습니다. 각자 자기 Slice에 자기 값을 넣었으니까요. Redux가 요구하는 그 번거로운 사전 정의가, 사실은 이 팀에 가장 필요했던 절차였습니다.
🚦 왜 action과 reducer로 나누는가
Redux의 구조가 왜 이렇게 번거로운지는, 무엇을 막으려는 장치인지 보면 이해가 됩니다.
글의 설명을 다시 보겠습니다.
"무엇을 하려는가"와 "그러면 어떻게 바뀌는가"를 분리한 구조입니다.
조각들은 값을 직접 못 바꿉니다. 대신 "로그인했다"는 사실만 알립니다(action). 그 사실을 받아서 상태를 실제로 어떻게 고칠지는 한곳(reducer)에서만 결정해요.
이렇게 하면 얻는 게 분명합니다. 값이 바뀌는 코드가 한 군데밖에 없습니다. 화면이 이상할 때 reducer만 보면 되고, "누가 이걸 바꿨지"를 뒤질 필요가 없어요.
3회차에서 이 팀이 겪은 문제가 바로 그 반대 상황이었습니다. 각 조각이 서버에서 직접 값을 받아 자기 칸에 넣었으니, 값이 바뀌는 자리가 조각 수만큼 있었던 겁니다. 그래서 어긋났을 때 어디를 봐야 할지 알 수 없었고요.
Redux가 번거로운 이유가 여기 있습니다. 번거로움이 곧 제약이고, 그 제약이 추적 가능성을 만듭니다. 그런데 이 제약은 팀이 "무엇을 공유할지"를 합의했을 때만 작동합니다. 합의 없이 도구만 들이면 각자 자기 reducer를 만들게 되고, 결국 한곳이 아니라 여러 곳이 됩니다.
⚖️ 세 도구는 무엇을 대가로 치르는가
세 도구를 나란히 놓으면 편의와 통제의 교환이 보입니다.
| 설정 부담 | 얻는 것 | |
|---|---|---|
| Context API | 거의 없음 | props drilling 해소 |
| Recoil | 적음 | 값 단위 구독, 불필요한 리렌더링 없음 |
| Redux | 많음 | 변경 지점이 한곳, 추적 가능 |
설정이 적을수록 통제가 약합니다. Context API는 바로 쓸 수 있지만 값 하나가 바뀌면 아래가 전부 다시 그려지고, 누가 언제 바꿨는지도 추적되지 않아요. Redux는 반대고요.
그래서 글의 결론이 "소규모 프로젝트에 적합"과 "설정할 내용이 많았다"로 갈리는 겁니다. 프로젝트 크기와 팀 인원이 선택 기준이 되는 이유예요. 혼자 만드는 작은 화면이라면 추적 가능성보다 속도가 중요하고, 여러 명이 붙는 프로젝트라면 반대입니다.
이 팀은 인원은 여럿인데 시간이 하루뿐이었습니다. 도구는 여럿에 맞는 걸 골랐는데, 그 도구를 쓰기 위한 합의에 쓸 시간이 없었어요.
📝 원문에서는 이렇게 정리합니다
Redux Toolkit이 왜 나왔는지에 대한 설명이 명료합니다. 원래 Redux는 일곱 단계를 거쳐야 했어요.
→ 5) reducer생성 → 6) 스토어 생성 → 7) 컴퍼넌트에 구독생성
그리고 Toolkit이 이걸 어떻게 줄였는지를 이어서 적습니다.
2) configureStore: reducer의 내용을 모아서 store설정 간편화
useSelector: 리듀서에 접근하여 상태값을 가져오는 hook (connect 생략가능)
useDispatch : action의 내용을 리듀서로 보내는 hook
일곱 단계가 세 개로 줄었습니다. 그런데 결론은 이렇습니다.
직접 써본 사람만 쓸 수 있는 문장입니다. 공식 문서는 Toolkit이 얼마나 간편해졌는지를 말하지, 그래도 여전히 많다고는 말하지 않거든요.
큐레이터 노트
이 글이 좋은 이유는 비교의 틀이 일정하다는 점입니다.
세 도구를 전부 같은 두 항목으로 봅니다. ① 관리할 상태의 종류 및 값 설정 ② 컴포넌트에 상태값 변경. 그리고 두 번째 항목은 다시 "외부의 변화를 컴포넌트에 반영"과 "내부의 변화를 전역으로 공유"로 나눠요.
도구 비교 글이 흔한데 대부분 각 도구의 소개를 이어 붙인 형태입니다. 그러면 읽고 나서도 뭘 골라야 할지 모릅니다. 같은 질문을 세 번 던져야 비교가 됩니다.
그리고 이 글의 진짜 값어치는 순서에 있다고 생각합니다. 도구를 고르고, 실패하고, 그다음에 조사했어요. 순서가 거꾸로죠. 그런데 그렇게 쓴 비교 글은 다릅니다. 각 도구의 단점 항목이 유난히 구체적인 게 그 때문이에요. Context API의 리렌더링 문제도, Redux의 설정 부담도, 겪어보고 쓴 문장입니다.
💡 세 편을 겹쳐 읽으며
세 글이 다루는 기술이 다릅니다. JPA, Spring Data JPA, 그리고 React 상태 관리 도구들.
그런데 셋 다 같은 질문 앞에 서 있습니다. 어디까지 맡길 것인가.
영속성 컨텍스트는 UPDATE 쿼리를 대신 써줍니다. Spring Data JPA는 구현체를 대신 만들어줍니다. Redux는 상태를 대신 지켜줍니다. 셋 다 개발자가 하던 일을 프레임워크가 가져간 거예요.
그리고 셋 다 가져간 만큼 안 보이게 됐습니다.
쿼리를 안 쓰니 언제 몇 개가 나가는지 모릅니다. 구현체를 안 만드니 메서드 이름 규칙을 어기면 어디서 틀렸는지 찾기 어렵습니다. 상태를 스토어에 맡기니 값이 어디서 바뀌었는지 추적해야 합니다.
세 분 다 이 대가를 글에 적었다는 게 공통점입니다.
이범수 님은 쓰기 지연과 flush의 차이를 짚었습니다. 형기상 님은 메서드 이름 방식이 "파라미터 1~2개까지가 적당"하다는 한계선을 적었어요. 이지은 님은 Toolkit으로도 "설정할 내용이 많았다"고 썼습니다.
기술 소개 글에서 가장 흔히 빠지는 게 이 부분입니다. 좋은 점만 적으면 도입 근거는 되지만 판단 근거는 되지 않습니다. 언제 쓰지 말아야 하는지를 모르면, 결국 안 맞는 곳에 쓰게 되거든요. 이지은 님 팀이 하루 만에 Redux를 고른 것처럼요.
이번 회차에서 가져갈 것
1. 자동으로 되는 것의 실행 시점을 알아두기.
변경 감지는 편하지만 UPDATE가 나가는 순간은 코드에 안 보입니다. 지금 쓰는 프레임워크에서 "내가 안 썼는데 실행되는 것"이 무엇인지, 그게 언제 실행되는지 한 번 확인해보면 좋겠습니다. 성능 문제는 대개 거기서 나옵니다.
2. 편의 기능의 한계선을 같이 적기.
형기상 님이 "파라미터 1~2개까지 적당"이라고 적어둔 것처럼요. 기능을 배울 때 "어디까지 이 방식으로 갈 수 있나"를 함께 메모해두면, 나중에 갈아탈 시점을 놓치지 않습니다.
3. 비교는 같은 질문으로 하기.
이지은 님은 세 도구에 똑같은 두 질문을 던졌습니다. 도구를 나란히 소개하면 목록이 되고, 같은 질문을 반복하면 비교표가 됩니다. 후자만 선택에 쓸 수 있어요.
4. 못 푼 질문을 남겨두기.
형기상 님의 필기에는 "동적 쿼리는 어떻게?"라는 물음표가 남아 있고, 그게 다음 편의 주제가 됐습니다. 모르는 것을 지우면 정리가 깔끔해지지만, 남겨두면 다음에 무엇을 공부할지가 정해집니다.
1기는 2024년 5월에 시작해 8월에 마무리된 그로스로그의 첫 기수였습니다. 4회차쯤 되면 각자 파고 있는 주제가 깊어집니다. 이 회차 세 편이 전부 "프레임워크 안쪽"을 들여다보는 글인 것도 우연은 아닐 거예요.
편하게 쓰던 것이 어느 날 안 되기 시작하면, 그때부터는 안쪽을 알아야 합니다. 세 분 다 그 지점에 도착해 있었습니다.
좋은 글 남겨주신 이범수 님, 형기상 님, 이지은 님께 감사드립니다. 그리고 아무 전례도 없던 첫 기수에서 묵묵히 기록을 이어가주신 모든 1기 멤버분들께도요! 😊
다음 큐레이션으로 다시 찾아오겠습니다! 🌱
- 에디터 · 성장일지 큐레이터 -
※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.
'성장일지' 카테고리의 다른 글
| [GL 1기 6회차] 아직 아무것도 안 만들었는데 프로젝트는 시작됐다 — 서버 구성도, 설명 자료, 색상 팔레트 🌱 (0) | 2026.08.09 |
|---|---|
| [GL 1기 5회차] 본체를 열지 않고 기능을 더하는 법 — Spring AOP, Visitor 패턴, 레벨 시퀀스 🌱 (0) | 2026.08.09 |
| [GL 1기 3회차] 경계를 먼저 긋지 않으면 — 언리얼의 메시와 충돌, Redux 상태 동기화, 쇼핑몰 첫걸음 🌱 (0) | 2026.08.09 |
| [GL 1기 2회차] 학교에서 배운 것과 현업 사이 — 과목 별점 후기, 워크북 족보론, 강의 밖의 스프링 🌱 (0) | 2026.08.09 |
| [GL 1기 1회차] 접었던 블로그를 다시 연 세 사람 — 사진 3요소, 깃허브 학생 인증, 완주 다짐 🌱 (0) | 2026.08.09 |