왜 iframe끼리는 DOM을 직접 공유하지 못하는지에 대해 DOM과 iframe를 정리해보았다.

같은 도메인이라면 contentWindow, 다른 도메인이면 postMessage를 사용하면 된다.

1. DOM (Document Object Model) 이란?

DOM은 브라우저가 웹 페이지를 이해하고 조작할 수 있도록 HTML 문서의 구조를 나무(Tree) 형태의 객체로 만든 것입니다.

  • 비유하자면: HTML 코드가 집의 '설계도'라면, DOM은 그 설계도를 보고 실제로 지어진 '건물(방, 가구, 문 등의 구조)'입니다.
  • 자바스크립트는 이 DOM을 통해 웹 페이지의 글자를 바꾸거나, 요소를 숨기는 등의 동적인 조작을 할 수 있습니다.
  • 중요한 점: 브라우저에 로드된 각각의 HTML 파일(웹페이지)은 저마다 독립된 DOM을 하나씩 가집니다. 하나의 페이지만 띄워도 DOM이 생기고, 화면이 여러 개면 각각 따로 DOM이 생깁니다.

2. iframe (Inline Frame) 이란?

iframe은 현재 웹 페이지 안에 또 다른 HTML 페이지를 통째로 쏙 집어넣어 보여주는 태그입니다 (<iframe>).

  • 비유하자면: 웹 페이지 안에 작은 모니터(창문)를 하나 더 달아서, 그 안에 완전히 별개의 웹사이트나 문서를 띄워놓는 것과 같습니다. (예: 블로그에 유튜브 영상을 띄울 때 사용)
  • iframe의 정체: iframe은 단순히 "다른 HTML 화면(또 다른 DOM 세계)을 내 화면 안에 쏙 집어넣기 위해 쓰는 도구(상자)"일 뿐입니다.

3. "iframe끼리는 DOM을 직접 공유하지 않는다"의 의미

부모 페이지(메인 페이지)와 자식 페이지(iframe 안의 페이지)는 각자 고유의 독립된 DOM을 가지고 따로 놉니다.

따라서 부모가 자식의 DOM을 마음대로 직접 제어할 수 없습니다. 이는 보안과 독립성 때문입니다. 만약 아무 웹사이트나 내 페이지에 iframe으로 들어왔을 때 내 사이트의 DOM(로그인 정보 등)을 마음대로 조작할 수 있다면 엄청난 보안 사고가 발생하기 때문에 브라우저가 원천적으로 차단하는 것입니다.

4. 자식에서 부모로 신호를 보내려면? postMessage

DOM을 직접 공유할 수 없기 때문에, 자식이 부모에게 뭔가 요청하거나 데이터를 전달하려면 postMessage라는 안전한 통신 기능을 사용해야 합니다.

  • 비유하자면: 자식이 부모 집 문을 무단으로 따고 들어가는(직접 조작) 대신, 우체통(메시지 전송)에 편지를 넣어 보내고 부모가 그것을 확인해 스스로 처리하는 방식입니다.

 

💡 예시 소스코드

① 부모 페이지 (index.html)

부모는 자식으로부터 메시지가 오는지 항상 귀를 기울이고(이벤트 리스너) 있어야 합니다.

HTML
 
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>부모 페이지</title>
</head>
<body>
    <h1>여기는 부모 페이지입니다.</h1>
    <p id="parent-text">아직 자식의 메시지를 받지 못했습니다.</p>

    <!-- iframe 삽입 -->
    <iframe id="my-iframe" src="child.html" width="400" height="200"></iframe>

    <script>
        // 부모는 자식으로부터 메시지가 날아오기를 기다립니다.
        window.addEventListener('message', function(event) {
            console.log("자식에게서 온 데이터:", event.data);

            // 자식이 보낸 데이터가 "changeText"라면 부모의 DOM을 수정합니다.
            if (event.data === "changeText") {
                document.getElementById('parent-text').innerText = "자식의 요청을 받아 부모가 텍스트를 바꿨습니다!";
            }
        });
    </script>
</body>
</html>

② 자식 페이지 (child.html - iframe 내부)

자식은 window.parent(부모 창 객체)를 통해 부모에게 메시지를 보냅니다.

HTML
 
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>자식 페이지</title>
</head>
<body style="background-color: #fff9c4;">
    <h2>여기는 iframe 안쪽(자식)입니다.</h2>
    <button id="send-btn">부모에게 말 걸기</button>

    <script>
        document.getElementById('send-btn').addEventListener('click', function() {
            // window.parent를 통해 부모 창에 메시지 전달
            // postMessage("보낼 메시지", "허용할 도메인(*는 전부 허용)")
            window.parent.postMessage("changeText", "*");
        });
    </script>
</body>
</html>

📌 요약

  1. DOM: HTML 구조가 브라우저에 의해 객체화된 것. HTML 파일마다 하나씩 독립적으로 생김.
  2. iframe: 내 페이지 안에 다른 HTML 페이지를 띄우는 상자 역할.
  3. DOM 공유 불가: 보안을 위해 부모와 자식은 서로의 DOM을 직접 조작할 수 없음.
  4. 해결책 (postMessage): 서로 안전하게 메시지를 주고받기 위해 브라우저가 제공하는 공식 소통 방법.

Oracle 기반 프로젝트를 PostgreSQL로 마이그레이션하거나, 다중 DB를 지원할 때 흔히 겪는 문자열 ↔ 숫자 암묵적 형변환(Implicit Casting) 오류 정리글입니다.

 

💡 한 줄 요약

Java에서 숫자형 파라미터를 String("2")으로 넘어온 상태로 MyBatis에 바인딩했습니다. Oracle은 '2' → NUMBER 암묵적 형변환을 허용하지만, PostgreSQL은 엄격하게 거부하여 에러가 발생했습니다.

 

1. 증상 및 에러 로그

🚨 PostgreSQL Error Log

SQL
 
ERROR: column "insert_no" is of type bigint but expression is of type character varying
Hint: You will need to rewrite or cast the expression.

📝 실제 실행된 SQL 로그

SQL
 
INSERT INTO TB_IAM_TEMP_SAVE_MASTER ( ..., INSERT_NO, ... )
VALUES ( 16::bigint, ..., '2', CURRENT_TIMESTAMP, ... )
--                        ^^^ 문자열('varchar')로 바인딩됨!
  • PostgreSQL 컬럼 타입: bigint (또는 integer)
  • 넘어온 데이터: '2' (varchar)
  • 특이사항: 동일한 Java 로직과 SQL 흐름인데 Oracle에서는 정상 실행됨.

 

2. 원인 분석: Oracle vs PostgreSQL

구분Oracle / PostgreSQL

 

컬럼 타입 NUMBER(38,0) BIGINT / INT
'2' (문자열) → 숫자 처리 암묵적 변환 허용 (OK) 암묵적 변환 거부 (Error)
결과 정상 Insert varchar → bigint 형변환 에러 발생
특징 비교적 타입 검사에 관대함 Prepared Statement 및 타입 검사가 엄격함
 

3. 자주 헷갈리는 Q&A

Q1. jdbcType=BIGINT를 붙였는데 왜 변환이 안 되나요?

A. MyBatis의 jdbcType 설정은 Java의 String을 Long으로 자동 변환해 주지 않습니다.

jdbcType은 주로 값이 null일 때 DB에 어떤 SQL Type으로 넣어줄지 알려주는 힌트 역할을 합니다. MyBatis는 기본적으로 Java 변수의 실제 데이터 타입을 보고 setXxx()를 결정합니다.

XML
 
<!-- Java 필드가 String이면 jdbcType을 BIGINT로 적어도 setString()이 호출되어 '2'로 찍힙니다. -->
#{insertNo, jdbcType=BIGINT} 

Q4. 그럼 jdbcType=BIGINT는 지워도 되나요?

A. 상황에 따라 다릅니다.

  1. SQL에서 ::bigint 명시적 캐스트를 수행하는 경우 ➔ 없어도 됨
  2. Java에서 이미 Long 타입으로 넘겨주는 경우 ➔ 없어도 됨
  3. 해당 파라미터에 null이 들어갈 가능성이 있는 경우 ➔ jdbcType을 명시해 주는 것이 안전함

4. 해결 방법 : SQL에서 명시적 형변환

MyBatis 쿼리문에서 PostgreSQL 전용 캐스트(::bigint)를 붙여줍니다.

 
INSERT INTO TB_IAM_TEMP_SAVE_MASTER (
    INSERT_NO
) VALUES (
    #{insertNo}::bigint
)

 

 

📌 최종 요약

  1. Oracle과 PostgreSQL의 차이: Oracle은 '2' → NUMBER 자동 변환을 해주지만, PostgreSQL은 strict하여 거부함.
  2. jdbcType=BIGINT의 오해: Java의 String을 자동으로 숫자로 변환해 주는 옵션이 아니며, 단순 NULL 처리 힌트에 가까움.
  3. 실무 해결 방안:
    • SQL 매핑 파일에서 #{insertNo}::bigint 처리
    • 또는 Java 레이어에서 Long 타입으로 캐스팅 후 바인딩

 

 

1. 시퀀스 (Sequence): 테이블 생성 시 기본값으로 참조하므로 가장 먼저 생성합니다. (독립 객체)

2. 테이블 (Table): 데이터 구조의 기본 바탕입니다.

3. 데이터(Data)

4. 인덱스 (Index) & 제약조건 (PK/FK): 테이블이 존재해야 그 위에 생성할 수 있습니다.

5. 뷰 (View) & 함수/프로시저: 테이블 및 인덱스 구조가 다 갖춰진 후 마지막에 올립니다.

 

 

추가로, 백업하여 dump.sql 파일을 생성하여 마이그레이션 할 db에 복원하는게 더 단순하고 편하다.

폐쇄망에서 복원하는 경우 외부 통신이 필요없는 것으로 아는데 외부 통신을 시도한다면 아래와 같이 설정이 필요하다.

🛠️ 폐쇄망에서의 해결 방법 (3가지)

방법 1. DBeaver에 네이티브 클라이언트 경로 직접 지정 (GUI 사용 시)

PC에 설치된 PostgreSQL의 bin 폴더 경로를 디비버에 지정해 주면 외부 다운로드를 시도하지 않습니다.

  1. 디비버 상단 메뉴 Window ➔ Preferences 이동
  2. Connections ➔ Drivers ➔ PostgreSQL 선택 (또는 좌측 데이터베이스 연결 우클릭 ➔ Edit Connection)
  3. Local Client 항목에서 Browse를 클릭하여 내 PC의 PostgreSQL bin 폴더 지정
  4. (예: C:\Program Files\PostgreSQL\16\bin)
  5. 적용 후 다시 복원을 시도합니다.

 

 

 

'개발환경' 카테고리의 다른 글

[Tomcat] 콘솔 한글 깨짐 증상 해결  (0) 2026.07.30
DBMS별 JDBC driver명, URL 형식  (1) 2024.11.06

인텔리제이(IntelliJ)나 로컬 PC 이클립스 환경에서 잘 동작하던 Maven 프로젝트를 압축하여 폐쇄망 PC로 들고 오거나 패키지.zip으로 통째로 말아 이동했을 때, 구동 시 원본 PC의 C드라이브 절대 경로를 찾으면서 ClassNotFoundException이나 빌드 에러가 발생하는 현상이 있습니다.

pom.xml 내부의 경로를 아무리 수정해도 특정 jar가 이전 개발 PC의 경로를 따라가는 원인과, 이를 근본적으로 해결하는 방법을 정리합니다.

1. 현상 및 원인 분석

❓ 원인: .factorypath 파일과 Enable project specific settings

프로젝트 루트 폴더를 확인해 보면 .factorypath라는 이클립스 전용 XML 설정 파일이 존재하는 경우가 많습니다.

<!-- .factorypath 파일 예시 -->
<factorypath>
    <factorypathentry kind="EXTJAR" id="C:\Users\User\.m2\repository\org\projectlombok\lombok\1.18.30\lombok-1.18.30.jar" enabled="true" runInBatchMode="false"/>
</factorypath>

이 파일 내부에는 위와 같이 개발 PC의 로컬 메이븐 리포지토리(C:\Users\계정명\.m2\...) 절대 경로가 하드코딩되어 들어갑니다.

이 파일이 생기는 메커니즘

  1. 이클립스 기본 상태에서는 이 기능이 꺼져(Off) 있습니다.
  2. 하지만 pom.xml에 Lombok, MapStruct, QueryDSL 등 아노테이션 프로세서(Annotation Processor) 관련 라이브러리가 포함된 상태로 프로젝트를 Import하면, 이클립스(m2e 플러그인)가 프로젝트의 Annotation Processing > Enable project specific settings 옵션을 자동으로 활성화(On)시킵니다.
  3. 그 순간 이클립스가 IDE 차원에서 아노테이션을 처리해주기 위해 현재 PC의 jar 경로를 읽어 .factorypath 파일에 기록하게 됩니다.
  4. 이 상태로 프로젝트 전체(또는 이클립스 JDT가 컴파일한 결과)를 폐쇄망으로 들고 가면, 폐쇄망 PC에는 해당 C드라이브 경로가 존재하지 않아 구동 실패 및 에러가 발생하게 됩니다.

 

2. 해결 방법: Maven 표준 빌드로 원복하기

이클립스 JDT 컴파일러의 개입을 막고 Maven 표준 빌드가 순수하게 라이브러리를 제어하도록 설정해야 합니다.

Step 1. 패키지 시 무조건 이클립스 Annotation Processing 설정 해제 (Off)

  1. 이클립스에서 프로젝트 마우스 우클릭 > Properties 진입
  2. 좌측 메뉴: Java Compiler > Annotation Processing 선택
  3. 우측 상단 Enable project specific settings 체크 해제 (Off)
  4. 하위 메뉴인 Factory Path에서도 동일하게 체크 해제 (Off) 후 Apply and Close 클릭

Step 2. 잔재 설정 파일 삭제 (중요)

설정을 해제했더라도 기존에 생성된 절대 경로 파일이 남아있으면 안 됩니다.

프로젝트 폴더(또는 이클립스 Explorer)에서 아래 파일/폴더를 직접 삭제해 줍니다.

  • .factorypath (가장 중요: 원본 PC 경로 저장 파일)
  • target/ 폴더 (기존 빌드 잔재 삭제)

 

Step 3. Maven 패키징 재실행

package -DskipTests
  1. Offline 체크 > Run 실행 > 하단 Console에 BUILD SUCCESS 확인
  2. 프로젝트 내 target/ 폴더에 생성된 결과물을 폐쇄망 서버에서 구동시킵니다.

 

3. 한 줄 요약

폐쇄망 전송 전 Enable project specific settings 해제(Off) 및 .factorypath 파일 삭제를 습관화하면 환경 이동 시 발생하는 빌드 경로 문제를 깔끔하게 예방할 수 있습니다. 이걸 몰라서 몇시간이나 시간을 소요했습니다 ,..

+ Recent posts