’‘‘주니어가 처음 ’‘‘JdbcTemplate’’ 을 만났을 때 — ’‘’‘jdbcTemplate.query(sql, rowMapper)’‘’’’‘‘두 인자 중 ’‘‘어느 게 ’‘‘템플릿’’ 이고 어느 게 ’‘‘콜백’’ 인지 ’‘‘한 번에 안 보인다’‘’‘. 그리고 *’‘‘2 년 뒤 ’‘‘Spring Security 필터 체인을 디버깅하다가 ’‘HttpServletRequestWrapper* 를 만났을 때 — ’‘그게 ’‘데코레이터’‘’’* 라는 걸 ’‘‘배운 적은 있지만 ’‘자기 코드와 어떻게 연결되는지* 까지 가는 데 ’‘‘또 ’‘‘한 학기’‘’‘’‘’‘’‘’’* 가 걸린다. 이 ’‘세 패턴은 ’‘한 가족’‘’‘’‘’‘’‘’‘’‘’‘’’’‘모두 ’‘‘Hollywood Principle (’‘Don’t call us, we’ll call you’‘’‘) 의 변주다’‘’‘’‘’‘’‘’‘. 시니어가 주니어에게 ’‘한 번에*** 풀어보는 글.

이 글은 ’‘Java/Spring 코드를 만지는 1-2 년차 주니어’‘’‘’‘’‘’‘’‘’‘’’* 가 ’‘세 패턴을 ’‘자기 손가락 끝에서 ’‘연결’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’‘** 시킬 수 있도록 ’‘짧은 코드 + 실전 인용’‘’‘’‘’‘’‘’‘’‘’’ 로 구성. ’‘ASCII 다이어그램 / Spring 의 실제 클래스 / 본인 코드의 ’‘*적용 가능 지점’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’ 까지.


1. ’‘한 가족인 이유 — ’‘Hollywood Principle’‘’‘’‘’‘’‘’‘’’

주니어가 쓰는 코드:  "내가 라이브러리를 *''*'**호출*''*''*''* 한다."

시니어가 쓰는 코드:  "라이브러리가 내 코드를 *''*'**호출*''*''*''* 한다."
                                                ↑
                                "Don't call us, we'll call you."

’‘역방향 흐름’‘’‘’’’‘세 패턴의 공통 본질’‘’‘’‘’‘’‘’‘*:

패턴 누가 누구를 호출하는가
템플릿 메소드 추상 클래스의 ’‘고정 메소드(템플릿) 가 ’‘서브클래스의 hook 을 호출’‘’‘’‘’‘’‘’‘’‘’‘’’
콜백 라이브러리의 ’‘고정 메소드가 ’‘클라이언트가 전달한 함수를 호출’‘’‘’‘’‘’‘’‘’‘’‘’’
데코레이터 ’‘래퍼가 ’‘원본 호출을 ’‘*가로채서 *’‘‘추가 작업 후 위임’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*

= ’‘제어 흐름이 ’‘클라이언트 → 라이브러리 ’‘‘단방향이 아니라 ’‘상호 호출’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. *’‘프레임워크는 항상 이런 패턴을 쓴다’‘’‘’‘’‘’‘’‘’‘. ’‘Spring 도, Servlet 도, JDBC 도’‘’‘’‘’‘*.

2. ’‘템플릿 메소드 패턴 — ’‘‘뼈대를 잡고 ’‘‘빈칸만 채우게 한다’’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*

2.1. 정의 — ’‘*고전적 GoF 정의’‘’‘’’*

’‘알고리즘의 ’‘뼈대’‘’’* 를 ’‘상위 클래스에 정의’‘’’* 하고, ’‘구체적 단계는 ’‘서브클래스에서 ’‘오버라이드’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’* 하게 한다’‘’‘. — *’‘*GoF, Design Patterns (1994)’‘’‘’’*

2.2. ’‘*가장 단순한 코드 예시’‘’‘’’*

// 추상 템플릿 — 알고리즘 *''*'전체 흐름*''*''* 을 *''*'고정*''*''*
abstract class HttpRequestProcessor {

    // ★ 템플릿 메소드 — *''*'final 로 *''*'오버라이드 금지*''*''*''*''*''*''*
    public final HttpResponse process(HttpRequest req) {
        if (!authenticate(req)) {
            return HttpResponse.unauthorized();
        }
        log(req);
        var result = handle(req);          // ← *''*'서브클래스 책임*''*''*
        return wrap(result);
    }

    protected abstract HttpResponse handle(HttpRequest req);  // hook

    protected boolean authenticate(HttpRequest req) {         // default
        return req.hasHeader("Authorization");
    }

    private void log(HttpRequest req) { /* 공통 로깅 */ }
    private HttpResponse wrap(HttpResponse r) { /* 공통 래핑 */ return r; }
}

// 서브클래스 — *''*'**빈칸*''*''* 만 채움
class UserController extends HttpRequestProcessor {
    @Override
    protected HttpResponse handle(HttpRequest req) {
        // 사용자 핸들러
        return HttpResponse.ok(...);
    }
}

2.3. ’‘실전 — Spring 의 ’‘JdbcTemplate’‘’‘’‘’‘’‘’’*

JdbcTemplate.query(sql, rowMapper)’‘*내부’‘’‘’‘*:

1. Connection 획득      ← 공통 (인프라)
2. PreparedStatement 준비 ← 공통
3. SQL 실행              ← 공통
4. ResultSet → 객체     ← ★ rowMapper 가 *''*'**책임*''*''*''*
5. ResultSet close      ← 공통
6. Connection close     ← 공통

’‘6 단계 중 ’‘4 번만 ’‘호출자가 채우면 됨’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. *’‘나머지 5 단계는 ’‘JdbcTemplate 이 ’‘보장’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘*.

List<User> users = jdbc.query(
    "SELECT id, name FROM users WHERE active = true",
    (rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"))
);

여기서 ’‘람다’‘’‘’’’‘RowMapper 의 콜백’‘’‘’‘’‘. → *’‘자연스럽게 ’‘3 절 (콜백) 로 연결’‘’‘’‘’‘’‘’‘’‘’‘’‘.

2.4. ’‘Spring 의 ’‘OncePerRequestFilter’‘’‘’‘’‘’‘’’*

public abstract class OncePerRequestFilter extends GenericFilterBean {
    public final void doFilter(...) throws ... {
        if (isAlreadyFiltered(request)) {
            chain.doFilter(request, response);
            return;
        }
        try {
            request.setAttribute(FILTERED, Boolean.TRUE);
            doFilterInternal(request, response, chain);  // ← hook
        } finally {
            request.removeAttribute(FILTERED);
        }
    }

    protected abstract void doFilterInternal(...);
}

= ’‘doFilter 가 ’‘final’‘’‘’’ + ’‘중복 호출 방지가 ’‘보장’‘’‘’‘’‘’‘’‘’‘’‘’‘. ’‘doFilterInternal 만 ’‘서브클래스가 채움’‘’‘’‘’‘’‘’‘’‘. ’‘Spring Security 의 ’‘모든 필터’‘’‘’’ 가 이 패턴.

2.5. ’‘*언제 쓰는가’‘’‘’’*

  • ’‘알고리즘 흐름이 ’‘‘고정’‘’‘’‘’‘’’ + ’‘일부 단계만 ’‘*다양’‘’‘’‘’‘’‘’‘’‘’’
  • ’‘보안·트랜잭션·로깅 같은 ’‘공통 부분이 ’‘*누락되면 안 되는 경우’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’
  • ’‘프레임워크가 ’‘호출자의 코드를 ’‘*제어하고 싶을 때’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’

3. ’‘콜백 패턴 — ’‘‘함수를 인자로 ’‘‘전달’’‘’‘’‘’‘’‘’‘’‘’’*

3.1. ’‘템플릿과 ’‘거의 같은 의도, ’‘‘다른 구현’‘’‘’‘’‘’‘’‘’‘’’*

템플릿 = ’‘서브클래싱’‘’‘’’ (상속). 콜백 = ’‘객체 / 함수 전달’‘’‘’’ (위임).

같은 문제, ’‘다른 도구’‘’‘’‘’‘*.

// 콜백 인터페이스
interface RowMapper<T> {
    T mapRow(ResultSet rs, int rowNum) throws SQLException;
}

// 콜백을 받는 *''*'**라이브러리*''*''*''*
class MyJdbc {
    <T> List<T> query(String sql, RowMapper<T> mapper) {
        // 1-3, 5-6 은 *''*'**MyJdbc 가 책임*''*''*''*
        // 4 만 mapper 에 *''*'**위임*''*''*''*
        ResultSet rs = ...;
        List<T> out = new ArrayList<>();
        int n = 0;
        while (rs.next()) {
            out.add(mapper.mapRow(rs, n++));  // ★ 콜백
        }
        return out;
    }
}

3.2. ’‘람다 시대 — ’‘Java 8 이후 ’‘*훨씬 자연스러움’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*

// Java 7 — 익명 클래스
jdbc.query(sql, new RowMapper<User>() {
    @Override
    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
        return new User(rs.getLong("id"), rs.getString("name"));
    }
});

// Java 8+ — 람다 (같은 의도, 짧음)
jdbc.query(sql, (rs, rowNum) ->
    new User(rs.getLong("id"), rs.getString("name"))
);

= ’‘람다 = 콜백의 ’‘아주 작은 형태’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. *’‘Java 8 이후 ’‘콜백 패턴이 ’‘*일상화’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘*.

3.3. ’‘Spring 의 ’‘TransactionTemplate’‘’‘’‘’‘’‘’’*

new TransactionTemplate(txManager).execute(status -> {
    // 트랜잭션 시작/커밋/롤백은 *''*'**Spring 이 처리*''*''*''*
    // 여기 람다는 *''*'**비즈니스 로직만***''*''*''*
    repository.save(order);
    return null;
});

= ’‘JdbcTemplate 와 ’‘완전히 같은 패턴’‘’‘’‘’‘’‘’‘’‘. ’‘프레임워크가 ’‘라이프사이클 보장’‘’’* + ’‘핵심 한 줄은 ’‘호출자가 채움’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘.

3.4. ’‘Strategy 패턴과의 ’‘차이’‘’‘’‘’‘’‘’‘’’

측면 Strategy Callback
의도 ’‘‘알고리즘 ’‘‘교체 가능’‘’‘’‘’‘’’ ’‘한 시점’‘’‘’‘’‘호출 위임’‘’‘’‘’‘’‘’’*
생명 주기 객체에 ’‘*저장’‘’‘’’* (필드) 한 호출에 ’‘*전달’‘’‘’’* (인자)
형태 ’‘*여러 메소드’‘’‘’’* 의 인터페이스 ’‘*한 메소드’‘’‘’’* (보통 functional interface)

Strategy 가 ’‘더 무거운 객체 단위 결정’‘’‘’‘’‘’‘’‘, **Callback 은 *’‘가벼운 한 시점의 위임**’‘’‘’‘’‘’‘’‘’‘.

3.5. ’‘*언제 쓰는가’‘’‘’’*

  • ’‘한 메소드의 ’‘일부분만 ’‘‘바꾸고 싶을 때’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*
  • ’‘상속 계층을 ’‘더 만들고 싶지 않을 때’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*
  • ’‘람다로 ’‘한 줄에 표현 가능할 때’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*

4. ’‘데코레이터 패턴 — ’‘‘원본을 ’‘*감싸서 *’‘‘기능을 ’‘‘덧붙인다’’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*

4.1. ’‘가장 직관적 예 — ’‘Java I/O’‘’‘’‘’‘’‘’’*

InputStream in = new FileInputStream("data.txt");           // 원본
InputStream buffered = new BufferedInputStream(in);          // ← 데코레이터
InputStream zipped = new GZIPInputStream(buffered);          // ← 데코레이터
DataInputStream data = new DataInputStream(zipped);          // ← 데코레이터

= ’‘4 단계 ’‘‘러시아 인형’‘’‘’‘’‘’‘’‘. 각 *’‘데코레이터’‘’’* 가 ’‘같은 인터페이스 (InputStream)’‘’‘’’’‘구현하면서’‘’’* ’‘*래핑 + 추가 기능’‘’‘’‘*.

read() 호출 → DataInputStream → GZIPInputStream → BufferedInputStream → FileInputStream
                                                                            ↑
                                                                        진짜 디스크 read

4.2. ’‘스프링의 ’‘HttpServletRequestWrapper’‘’‘’‘’‘’‘’’*

public class CachingRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;

    public CachingRequestWrapper(HttpServletRequest delegate) throws IOException {
        super(delegate);
        this.body = StreamUtils.copyToByteArray(delegate.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() {
        return new CachedServletInputStream(body);  // *''*'**캐싱된 body 반환*''*''*''*
    }
}

= ’‘원본 request 를 그대로 두고 ’‘감싸서’‘’’* ’‘body 를 ’‘여러 번 읽을 수 있게’‘’‘’‘’‘’‘’‘. *’‘로깅·검증·서명 검증’‘’’* 같은 곳에서 ’‘필수’‘’‘’‘.

4.3. ’‘AOP — ’‘데코레이터의 ’‘*자동화’‘’‘’‘’‘’‘’‘’‘’‘’’*

@Transactional 이 어떻게 동작하는가?

@Service
class OrderService {
    @Transactional
    public Order place(...) { ... }
}

→ Spring 이 ’‘프록시 데코레이터’‘’‘’’’‘자동 생성’‘’‘’‘:

class OrderService$$Proxy extends OrderService {  // ← 자동 생성된 데코레이터
    public Order place(...) {
        TransactionStatus tx = txManager.getTransaction(...);
        try {
            Order result = super.place(...);  // ← 원본 호출
            txManager.commit(tx);
            return result;
        } catch (Exception e) {
            txManager.rollback(tx);
            throw e;
        }
    }
}

= ’‘AOP = 컴파일 타임 + 런타임에 ’‘데코레이터가 ’‘자동 적용’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. *’‘개발자가 ’‘원본 코드에 ’‘아무것도 안 적어도’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘*.

4.4. ’‘*언제 쓰는가’‘’‘’’*

  • ’‘원본을 ’‘수정하지 않고’‘’‘’’ ’‘기능을 ’‘확장’‘’‘’‘’‘’‘’‘’‘’‘’’
  • ’‘여러 기능을 ’‘조합’‘’‘’’ 가능 (러시아 인형)
  • ’‘런타임에 ’‘선택적으로 ’‘*적용’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’

5. ’‘세 패턴이 ’‘한 코드에서 ’‘‘함께 사는 곳 — ’‘*Spring Security FilterChain’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’

HTTP request
    ↓
[OncePerRequestFilter (템플릿)]      ← 1단계: 템플릿
    ↓ doFilterInternal()
[AuthenticationFilter (데코레이터)]   ← 2단계: 데코레이터
    ↓ chain.doFilter(req, res)
[AuthorizationFilter (데코레이터)]    ← 3단계: 데코레이터
    ↓ chain.doFilter(req, res)
[DispatcherServlet]                  ← 4단계: 진짜 핸들러
    ↓
[Controller method]
    ↓ JdbcTemplate.query(sql, rowMapper)   ← 5단계: 콜백

한 흐름 안에서 ’‘세 패턴이 모두’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. ’‘Spring 에 능숙하다 = ’‘이 세 패턴의 ’‘조합 패턴 (composition pattern) 을 ’‘자유롭게 ’‘읽고 쓴다**’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘*.

6. ’‘주니어가 ’‘자주 헷갈리는 ’‘‘4 가지’‘’‘’‘’‘’‘’‘’’

6.1. ’‘‘템플릿이랑 콜백 ’‘언제 다른가?’’‘’‘’‘’‘’‘’’*

같은 의도 (Hollywood Principle), ’‘다른 ’‘메커니즘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘*.

  • ’‘여러 메소드가 ’‘같이 변하면’‘’‘’‘’‘’‘’‘’‘’’* → 템플릿 (서브클래싱)
  • ’‘한 시점만 변하면’‘’‘’’ → **콜백 (함수 전달)

6.2. ’‘‘데코레이터랑 상속 ’‘뭐가 다른가?’’‘’‘’‘’‘’‘’’*

상속 = ’‘컴파일 타임’‘’‘’’’‘고정’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. 데코레이터 = *’‘*‘런타임’‘’‘’’* 에 ’‘조합 가능’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘*.

// 상속 — 1 종류만
class GzippedBufferedFileInput extends FileInput { ... }

// 데코레이터 — 조합 자유
new GZIPInputStream(new BufferedInputStream(new FileInputStream(...)));
new BufferedInputStream(new GZIPInputStream(new FileInputStream(...)));

6.3. ’‘‘AOP 가 ’‘데코레이터**’‘’‘’’ 라고? 인터페이스도 안 만들었는데?’’‘’‘’’

’‘CGLIB 가 ’‘바이트코드 차원에서 ’‘서브클래스* 를 ’‘런타임 생성’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. *’‘인터페이스 없어도 ’‘데코레이터 가능’‘’‘’‘’‘’‘’‘’‘’‘. 단 *’‘final 클래스/메소드는 ’‘*프록시 불가’‘’‘’‘’‘’‘’‘’‘*.

6.4. ’‘‘JdbcTemplate 의 query 가 ’‘템플릿* 이라고? ’‘Java 클래스도 ’‘*JdbcTemplate’‘’’ 인데?’’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*

’‘Spring 의 명명이 ’‘‘템플릿 메소드 패턴’’‘’’* 의 ’‘Template’‘’‘’’ 에서 옴’‘’‘. *’‘JdbcTemplate 자체가 ’‘템플릿 메소드 ’‘객체화’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. *’‘RowMapper 는 ’‘그 안의 ’‘콜백’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. *’‘Spring 의 모든 ~Template 클래스 (JdbcTemplate, RestTemplate, JmsTemplate, RedisTemplate, …) 가 ’‘*같은 패턴’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘*.

7. ’‘실전 적용 가이드 — ’‘‘언제 어떤 걸 쓸까’’‘’‘’‘’‘’‘’’*

"공통 흐름을 만들고 *''*'**일부만 *''*'바꾸게 하고 싶다"
   ├─ 변하는 부분이 *''*'**여러 메소드*''*''* → 템플릿 (추상 클래스)
   ├─ 변하는 부분이 *''*'**한 시점*''*''* → 콜백 (함수형 인터페이스 + 람다)
   └─ 변하는 부분이 *''*'**선택적 + 조합 가능*''*''* → 데코레이터 (래퍼)

"기존 코드를 *''*'**건드리지 않고*''*''* 기능 추가"
   └─ 데코레이터 (또는 AOP)

"프레임워크의 *''*'**확장 지점*''*''* 을 만들고 싶다"
   ├─ 강제 보호 + 단계 고정 → 템플릿
   └─ 한 시점만 위임 → 콜백

8. ’‘AI 시대에 패턴이 ’‘‘더 중요해지는’ 이유’‘’‘’‘’‘’‘’’*

LLM 이 ’‘1 차 코드 생성’‘’‘’’ 을 잘 한다. ’‘그러나 ’‘구조를 ’‘‘어디서 어떻게 자를지’’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’’‘*여전히 사람의 일’‘’‘’‘’‘’‘*.

  • ’‘AI 가 생성한 코드’‘’‘’’’‘템플릿/콜백/데코레이터를 ’‘이해하지 못하면’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’’‘경계 위반 + 중복 + 강결합’‘’‘’‘’‘’’
  • ’‘시니어가 ’‘‘템플릿 자리’’‘’’* / ’‘‘콜백 자리’’‘’’* / ’‘‘데코레이터 자리’’‘’’* 를 ’‘미리 잡아두면’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’’‘AI 가 ’‘그 안에서만 ’‘작동**’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*

= ’‘패턴 = AI 시대의 ’‘가드레일’‘’‘’‘’‘’‘’‘’‘’‘’‘.

9. ’‘실전 사례 — ’‘본인 코드에서 ’‘‘적용 가능한 곳 찾기’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*

지금 본인 프로젝트에서 ’‘*5 분 안에 찾을 수 있는 곳’‘’‘’‘’‘’‘’‘:

  1. ’‘모든 컨트롤러에서 ’‘똑같이 반복하는 ’‘‘try-catch + logging’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’’‘OncePerRequestFilter (템플릿) 또는 ’‘@RestControllerAdvice’‘’’* (AOP 데코레이터)’‘’’*
  2. ’‘여러 곳에서 ’‘같은 SQL/REST 호출 후 ’‘다른 변환’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’* → ’‘메소드에 ’‘*Function<T, R>’‘’‘’‘’’ 받기 (콜백)*
  3. ’‘같은 객체에 ’‘선택적으로 ’‘캐싱 / 로깅 / 검증 추가’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’* → ’‘데코레이터 (러시아 인형)’‘’‘’’
  4. ’‘프레임워크의 ’‘확장 지점’‘’‘’’’‘필요할 때’‘’‘’’’‘abstract class + final 템플릿 메소드’‘’‘’‘’’*

10. ’‘결론 — ’‘‘한 가족, 세 얼굴’’‘’‘’‘’‘’‘’’*

세 패턴을 ’‘한 줄로 압축’‘’‘’‘’‘’‘’‘’‘’‘’‘:

  • ’‘템플릿’‘’‘’’’‘뼈대를 만들고 ’‘빈칸만 채우게 해라’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’
  • ’‘콜백’‘’‘’’’‘한 시점만 ’‘함수로 받아라’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*
  • ’‘데코레이터’‘’‘’’’‘기존을 건드리지 말고 ’‘감싸라’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*

세 패턴 ’‘모두 ’‘Hollywood Principle’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. *’‘Don’t call us, we’ll call you’’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘*.

’‘좋은 시니어는 ’‘‘템플릿 메소드 패턴이 뭡니까?’ 라는 질문에 ’‘JdbcTemplate 의 query 메소드 한 줄을 보여준다’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. *’‘좋은 주니어는 ’‘그 한 줄에서 ’‘‘아, 이게 그거구나’’‘’‘’’’‘알아챈다’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘.

’‘패턴은 ’‘암기가 아니라 ’‘‘알아챔’’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. ’‘암기는 ’‘ChatGPT 도 잘 한다’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. ’‘알아챔은 ’‘그 패턴이 살고 있는 코드* 를 ’‘한 번 ’‘손가락 끝까지 ’‘‘쫓아본 사람’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’* 에게서만 나온다’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘*.

’‘오늘 ’‘JdbcTemplate / RestTemplate / OncePerRequestFilter / HttpServletRequestWrapper’‘’‘’‘’‘’‘’’* 중 ’‘하나만 ’‘소스 코드까지 ’‘열어 보세요’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘. *’‘한 번에 ’‘시니어와 같은 풍경’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’ 이 보일 거예요.


더 읽을 거리

  • Design Patterns: Elements of Reusable Object-Oriented Software — GoF (1994). ’‘여전히 ’‘현대 표준**’‘’‘’‘’‘’‘’‘’’
  • Head First Design Patterns — Eric Freeman (개정판 2020). ’‘주니어용 가장 친절’‘’‘’‘’‘’‘’‘’’
  • Effective Java — Joshua Bloch. ’‘*Item 21-23 (인터페이스 vs 추상 클래스), Item 42-44 (람다와 메소드 참조)’‘’’
  • Spring Framework Documentation’‘*JdbcTemplate, RestTemplate, OncePerRequestFilter’‘’‘’‘’‘’‘’‘’’* 소스 코드 직접 읽기
  • Spring AOP Reference’‘데코레이터의 ’‘자동화 메커니즘**’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’*
  • Refactoring — Martin Fowler. ’‘‘Replace Conditional with Polymorphism’, ‘Extract Method’’‘’’* 가 ’‘세 패턴의 ’‘‘시작점’‘’’*

다음 글 예고: Strategy / Adapter / Facade — *’‘비슷해 보이는 세 패턴’‘’’* 의 ’‘미묘한 ’‘‘경계’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’’ (또 한 번의 ‘한 가족’ 시리즈)