[Java/MyBatis] MyBatis <foreach>에서 #{}와 ${} 차이, 그리고 item 두 번 쓰기

2025. 4. 5. 23:42·개발 (Development)/Java

MyBatis에서 <foreach>는 IN 조건이나 다중 INSERT와 같이 반복적인 SQL을 작성할 때 아주 유용하게 쓰입니다. 하지만 이 안에서 item을 여러 번 참조하거나, #{}와 ${} 중 어떤 걸 써야 하는지 헷갈릴 수 있습니다. 이 글에서는 그 차이점과 실전에서 어떻게 써야 안전하고 효율적인지 정리해보겠습니다.

#{} vs ${} 차이점

구분 #{} ${}
방식 PreparedStatement 방식 문자열 치환 방식
용도 값 바인딩 (WHERE, VALUES 등) 컬럼명, 테이블명 등 SQL 조각 삽입
장점 SQL 인젝션 방지, 자동 타입 변환, 성능 최적화 동적인 SQL 문 구조 만들기 가능
단점 컬럼명/테이블명 등 SQL 구조에는 사용 불가 SQL 인젝션 위험 있음, 성능 저하 가능
보안 안전 검증 없으면 매우 위험

예시 비교

<foreach collection="ids" item="id" separator=",">
  #{id}
</foreach>

→ SELECT * FROM user WHERE id IN (?, ?, ?)처럼 바인딩 처리됨

<foreach collection="ids" item="id" separator=",">
  ${id}
</foreach>

→ SELECT * FROM user WHERE id IN (1, 2, 3)처럼 그대로 문자열로 치환됨

💡 결론: 대부분의 경우 #{}를 사용하는 것이 안전하고 효율적입니다. ${}는 컬럼명/테이블명 동적 변경처럼 특별한 상황에서만 써야 하며, 입력값 검증은 필수입니다.

<foreach>에서 item을 두 번 이상 사용하는 법

item을 두 번 이상 쓰고 싶은 경우는 주로 다중 INSERT 구문에서 (#{item}, #{item})처럼 같은 값을 반복해서 사용할 때입니다. 이때 사용할 수 있는 방법은 다음과 같습니다.

1. 객체의 속성을 이용 (추천)

<foreach collection="items" item="item" separator=",">
  (#{item.id}, #{item.name})
</foreach>

각 item이 객체라면 필드를 나눠서 얼마든지 참조할 수 있습니다. 예: List<Item> 형태로 넘기면 자연스럽게 여러 필드를 사용할 수 있어요.

2. 단순 값일 경우 #{item} 반복 사용

<foreach collection="items" item="item" separator=",">
  (#{item}, #{item})
</foreach>

리스트 요소가 숫자나 문자열이면 이렇게 두 번 써도 문제 없습니다.

3. MyBatis 3.4 이상에서는 <bind>로 변수화

<foreach collection="items" item="item" separator=",">
  <bind name="v" value="item"/>
  (#{v}, #{v})
</foreach>

복잡한 상황에서 중간 변수로 처리할 수 있어 유용합니다.

마무리

  • #{}는 값 바인딩용 → 대부분의 경우 사용
  • ${}는 SQL 조각 삽입용 → 컬럼명/테이블명 등 특수한 경우에만
  • <foreach>에서 item을 여러 번 써야 한다면 객체 사용을 고려하거나 <bind> 활용
반응형

'개발 (Development) > Java' 카테고리의 다른 글

[Java] 사내망에서 Gradle 빌드 시 PKIX 인증서 오류 해결기 (feat. 프록시 & 인증서 등록)  (0) 2025.04.19
[Java/SpringBoot] Kubernetes 환경에서 Spring Boot 로그를 Pod별로 Rolling 하도록 설정  (0) 2025.04.19
[Java] 리스트의 요소가 다른 리스트에 포함되지 않는지 확인 (Collections.disjoint())  (1) 2025.03.22
[Java] API 요청 및 응답 시 비동기 처리  (0) 2024.12.30
[Java/JPA] JPQL 파라미터 바인딩 (위치 기반)  (0) 2024.04.11
'개발 (Development)/Java' 카테고리의 다른 글
  • [Java] 사내망에서 Gradle 빌드 시 PKIX 인증서 오류 해결기 (feat. 프록시 & 인증서 등록)
  • [Java/SpringBoot] Kubernetes 환경에서 Spring Boot 로그를 Pod별로 Rolling 하도록 설정
  • [Java] 리스트의 요소가 다른 리스트에 포함되지 않는지 확인 (Collections.disjoint())
  • [Java] API 요청 및 응답 시 비동기 처리
LoopThinker
LoopThinker
모르는 것을 알아가고, 아는 것을 더 깊게 파고드는 공간
  • LoopThinker
    CodeMemoir
    LoopThinker
  • 전체
    오늘
    어제
    • 분류 전체보기 (216) N
      • 개발 (Development) (151) N
        • Algorithm (1)
        • Angular (1)
        • AWS (4)
        • DeepSeek (2)
        • Docker (7)
        • Git (3)
        • Java (30)
        • JavaScript (4)
        • Kafka (5)
        • Kubernetes (4)
        • Linux (6)
        • PostgreSQL (37) N
        • Python (26)
        • React (3)
        • TypeScript (3)
        • Vue.js (5)
        • General (10)
      • 데이터 분석 (Data Analysis) (1)
      • 알고리즘 문제 풀이 (Problem Solving.. (27)
      • 자격증 (Certifications) (24)
        • ADsP (14)
        • 정보처리기사 (4)
        • Linux Master (5)
        • SQLD (1)
      • 기술 동향 (Tech Trends) (10)
      • 기타 (Others) (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    timescaledb
    JPA
    백준자바
    리눅스 마스터 2급 2차
    javascript
    JSON
    Linux
    MyBatis
    Kafka
    docker
    데이터분석
    deepseek
    Linux master
    typescript
    Vue.js
    자바
    pandas
    리눅스 마스터 2급
    백준알고리즘
    백준
    DevOps
    python
    ADsP
    Kubernetes
    springboot
    백준온라인저지
    AWS
    java
    PostgreSQL
    오답노트
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
LoopThinker
[Java/MyBatis] MyBatis <foreach>에서 #{}와 ${} 차이, 그리고 item 두 번 쓰기
상단으로

티스토리툴바