๐Ÿฝ Oracle CHAR ํƒ€์ž… ๊ณต๋ฐฑ ํŒจ๋”ฉ โ€” ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… & ์žฌ๋ฐœ ๋ฐฉ์ง€ ๊ฐ€์ด๋“œ



1. ํ˜„์ƒ

ํ•ญ๋ชฉ ๋‚ด์šฉ
๋ฐœ์ƒ ๊ธฐ๋Šฅ ๊ธฐ์ค€์ •๋ณด > ์ฝ”๋“œ๊ด€๋ฆฌ โ€” ์ฝ”๋“œ ๊ทธ๋ฃน ๋‹จ๊ฑด ์กฐํšŒ (selectGroupOne)
์ฆ์ƒ ์ „์ฒด ๋ชฉ๋ก ์กฐํšŒ(selectGroupList)๋Š” ์ •์ƒ, ๋‹จ๊ฑด ์กฐํšŒ๋Š” ํ•ญ์ƒ ๋ฐ์ดํ„ฐ ์—†์Œ(0๊ฑด)
P6SPY ๋กœ๊ทธ SQLยทํŒŒ๋ผ๋ฏธํ„ฐ ๋ชจ๋‘ ์ •์ƒ์œผ๋กœ ์ถœ๋ ฅ๋จ
MyBatis ๋งคํ•‘ ๊ฒฐ๊ณผ ๊ฐ์ฒด null ๋ฐ˜ํ™˜ (๋งคํ•‘ ์‹คํŒจ๊ฐ€ ์•„๋‹Œ 0๊ฑด ๋ฐ˜ํ™˜)

2. ์›์ธ ๋ถ„์„

2.1 Oracle CHAR ํƒ€์ž…์˜ ๊ณต๋ฐฑ ํŒจ๋”ฉ

Oracle์˜ CHAR(n) ์ปฌ๋Ÿผ์€ ์ž…๋ ฅ๊ฐ’์ด ์„ ์–ธ ๊ธธ์ด๋ณด๋‹ค ์งง์„ ๊ฒฝ์šฐ ์˜ค๋ฅธ์ชฝ์— ๊ณต๋ฐฑ์„ ํŒจ๋”ฉํ•˜์—ฌ ์ €์žฅํ•œ๋‹ค.


-- GROUP_CD ์ปฌ๋Ÿผ์ด CHAR(5)๋กœ ์„ ์–ธ๋œ ๊ฒฝ์šฐ

INSERT INTO T_CODEGROUP VALUES ('KOR', '002', '๊ฑฐ๋ž˜๊ตฌ๋ถ„');

-- โ†’ DB์—๋Š” GROUP_CD = '002  ' (๋’ค์— ๊ณต๋ฐฑ 2์ž๋ฆฌ ํŒจ๋”ฉ๋˜์–ด ์ €์žฅ)

๋””๋ฒ„๊ทธ ๋กœ๊ทธ๋กœ ํ™•์ธํ•œ ์‹ค์ œ ์ €์žฅ๊ฐ’:


[DEBUG] DB ์‹ค์ œ๊ฐ’ langCd='KOR' groupCd='002  ' groupName='๊ฑฐ๋ž˜๊ตฌ๋ถ„'

                                        โ†‘โ†‘ ๊ณต๋ฐฑ 2์ž๋ฆฌ ํŒจ๋”ฉ

2.2 Oracle์˜ ๋‘ ๊ฐ€์ง€ ๋ฌธ์ž์—ด ๋น„๊ต ๊ทœ์น™

Oracle์€ ๋น„๊ต ๋Œ€์ƒ์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋น„๊ต ๊ทœ์น™์„ ์ ์šฉํ•œ๋‹ค.

๋น„๊ต ์กฐํ•ฉ ์ ์šฉ ๊ทœ์น™ ๊ฒฐ๊ณผ ์˜ˆ์‹œ
CHAR = CHAR ๊ณต๋ฐฑ ํŒจ๋”ฉ ๋น„๊ต (blank-padded) '002' = '002 ' โ†’ TRUE
CHAR = VARCHAR2 ๋น„ํŒจ๋”ฉ ๋น„๊ต (non-padded) '002' = '002 ' โ†’ FALSE

2.3 JDBC ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” VARCHAR2๋กœ ์ „์†ก๋œ๋‹ค

MyBatis/JDBC๋Š” Java String ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ Oracle์— VARCHAR2๋กœ ๋ฐ”์ธ๋”ฉํ•œ๋‹ค.


์• ํ”Œ๋ฆฌ์ผ€์ด์…˜: groupCd = "002"    โ†’ JDBC PreparedStatement โ†’ Oracle VARCHAR2('002')

DB ์ €์žฅ๊ฐ’:    GROUP_CD = '002  ' โ†’ CHAR(5)

๋น„๊ต ์กฐํ•ฉ์ด CHAR = VARCHAR2๊ฐ€ ๋˜๋ฏ€๋กœ ๋น„ํŒจ๋”ฉ ๋น„๊ต ๊ทœ์น™์ด ์ ์šฉ๋˜์–ด:


-- ์‹ค์ œ๋กœ Oracle์ด ์ˆ˜ํ–‰ํ•˜๋Š” ๋น„๊ต

WHERE GROUP_CD = '002'

-- '002  ' (CHAR)  =  '002' (VARCHAR2)  โ†’ FALSE โ†’ 0๊ฑด ๋ฐ˜ํ™˜

2.4 selectGroupList๋Š” ์™œ ์ •์ƒ์ด์—ˆ๋‚˜

selectGroupList๋Š” WHERE ์ ˆ์ด ์—†๋Š” ์ „์ฒด ์กฐํšŒ์ด๋ฏ€๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. CHAR ํŒจ๋”ฉ ๊ฐ’ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜๋  ๋ฟ, ๋น„๊ต ์—ฐ์‚ฐ์ด ์—†์–ด ๋ฌธ์ œ๊ฐ€ ๋“œ๋Ÿฌ๋‚˜์ง€ ์•Š์•˜๋‹ค.


-- ๋ชฉ๋ก ์กฐํšŒ: WHERE ์—†์Œ โ†’ ๋น„๊ต ์—†์Œ โ†’ ํŒจ๋”ฉ๊ฐ’ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜

SELECT LANG_CD, GROUP_CD, GROUP_NAME FROM T_CODEGROUP ORDER BY LANG_CD, GROUP_CD

-- โ†’ ์ •์ƒ ๋™์ž‘

  

-- ๋‹จ๊ฑด ์กฐํšŒ: CHAR ์ปฌ๋Ÿผ์„ VARCHAR2 ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋น„๊ต โ†’ 0๊ฑด

SELECT ... FROM T_CODEGROUP WHERE GROUP_CD = '002'

-- โ†’ 0๊ฑด ๋ฐ˜ํ™˜ (DB์—๋Š” '002  '๋กœ ์ €์žฅ๋˜์–ด ์žˆ์Œ)


3. ๋กœ๊ทธ์—์„œ ์ •์ƒ์œผ๋กœ ๋ณด์˜€๋˜ ์ด์œ 

3.1 P6SPY๋Š” ๋ฐ”์ธ๋”ฉ ์ง์ „ ๊ฐ’์„ ๋กœ๊น…ํ•œ๋‹ค

P6SPY๋Š” PreparedStatement์˜ setString() ํ˜ธ์ถœ ์‹œ์  โ€” ์ฆ‰ Oracle ์„œ๋ฒ„์— ๊ฐ’์ด ์ „๋‹ฌ๋˜๊ธฐ ์ด์ „ ์— ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€๋กœ์ฑ„ ๋กœ๊น…ํ•œ๋‹ค.


[P6SPY ๋กœ๊ทธ]

select LANG_CD, GROUP_CD, GROUP_NAME from T_CODEGROUP

where LANG_CD = 'KOR' and GROUP_CD = '002'

๋กœ๊ทธ ์ƒ์œผ๋กœ๋Š” '002'๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ฐ”์ธ๋”ฉ๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ, Oracle ๋‚ด๋ถ€์—์„œ๋Š” ์ด ๊ฐ’์„ VARCHAR2 ํƒ€์ž…์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ '002  '(CHAR)์™€ ๋น„๊ตํ•˜๋ฏ€๋กœ 0๊ฑด์ด ๋ฐ˜ํ™˜๋œ๋‹ค.


P6SPY ๋กœ๊ทธ:  GROUP_CD = '002'    โ† ๋ˆˆ์—๋Š” ์ •์ƒ

Oracle ๋‚ด๋ถ€:  '002' (VARCHAR2) โ‰  '002  ' (CHAR)  โ† ์‹ค์ œ๋กœ๋Š” ๋ถˆ์ผ์น˜

3.2 SQL Developer์—์„œ๋Š” ์™œ ๋˜์—ˆ๋‚˜

SQL Developer์—์„œ ์ฟผ๋ฆฌ๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•  ๋•Œ๋Š” ๋ฆฌํ„ฐ๋Ÿด ๋ฌธ์ž์—ด '002'๊ฐ€ Oracle ๋‚ด๋ถ€์—์„œ CHAR ํƒ€์ž…์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์–ด ๊ณต๋ฐฑ ํŒจ๋”ฉ ๋น„๊ต ๊ทœ์น™์ด ์ ์šฉ๋˜๋ฏ€๋กœ ์ •์ƒ ์กฐํšŒ๋œ๋‹ค.

์‹คํ–‰ ํ™˜๊ฒฝ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… ๋น„๊ต ๊ทœ์น™ ๊ฒฐ๊ณผ
SQL Developer (๋ฆฌํ„ฐ๋Ÿด) CHAR ๊ณต๋ฐฑ ํŒจ๋”ฉ ๋น„๊ต ์ •์ƒ ์กฐํšŒ
JDBC PreparedStatement VARCHAR2 ๋น„ํŒจ๋”ฉ ๋น„๊ต 0๊ฑด ๋ฐ˜ํ™˜

์ด ์ฐจ์ด๊ฐ€ "์ฟผ๋ฆฌ๋Š” ์ •์ƒ์ธ๋ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋งŒ ์•ˆ ๋œ๋‹ค"๋Š” ํ˜ผ๋ž€์„ ์œ ๋ฐœํ–ˆ๋‹ค.


4. ์ ์šฉํ•œ ์ˆ˜์ • ๋‚ด์—ญ

4.1 ์ˆ˜์ • ์ „ (๋ฌธ์ œ ์ฝ”๋“œ)


<select id="selectGroupOne" resultMap="groupRm">

    SELECT LANG_CD, GROUP_CD, GROUP_NAME

    FROM T_CODEGROUP

    WHERE LANG_CD = #{langCd} AND GROUP_CD = #{groupCd}

</select>

4.2 ์ˆ˜์ • ํ›„ (์ˆ˜์ • ์ฝ”๋“œ)

CHAR ์ปฌ๋Ÿผ์— RTRIM์„ ์ ์šฉํ•˜์—ฌ ๊ณต๋ฐฑ ํŒจ๋”ฉ์„ ์ œ๊ฑฐํ•œ ํ›„ ๋น„๊ตํ•˜๊ณ , SELECT ์‹œ์—๋„ ์ •๊ทœํ™”๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.


<select id="selectGroupOne" resultType="kr.co.nh.atms.web.code.entity.CodeGroup">

    SELECT RTRIM(LANG_CD)  AS langCd,

           RTRIM(GROUP_CD) AS groupCd,

           GROUP_NAME      AS groupName

    FROM T_CODEGROUP

    WHERE RTRIM(LANG_CD) = #{langCd} AND RTRIM(GROUP_CD) = #{groupCd}

</select>

4.3 ๋™์ผ ๋ฌธ์ œ๊ฐ€ ์ ์šฉ๋œ ์ „์ฒด ๋ฒ”์œ„

T_CODEGROUP๊ณผ T_CODE์˜ CHAR ์ปฌ๋Ÿผ(LANG_CD, GROUP_CD, CODE)์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  WHERE ์ ˆ๊ณผ SELECT ์— ์ผ๊ด„ ์ ์šฉํ•˜์˜€๋‹ค.

์ฟผ๋ฆฌ ID ์ ์šฉ ๋‚ด์šฉ
codeCols (๊ณตํ†ต ์ปฌ๋Ÿผ) RTRIM + AS ๋ณ„์นญ ์ถ”๊ฐ€
codeWhere (๊ณตํ†ต ์กฐ๊ฑด) RTRIM(์ปฌ๋Ÿผ) = #{ํŒŒ๋ผ๋ฏธํ„ฐ}
selectCodeOne WHERE ์ ˆ RTRIM ์ ์šฉ
selectCodeByGroup WHERE ์ ˆ RTRIM ์ ์šฉ
updateCode WHERE ์ ˆ RTRIM ์ ์šฉ
deleteCode WHERE ์ ˆ RTRIM ์ ์šฉ
selectGroupList SELECT RTRIM ์ ์šฉ (๋ฐ˜ํ™˜๊ฐ’ ์ •๊ทœํ™”)
selectGroupOne SELECT + WHERE ๋ชจ๋‘ RTRIM ์ ์šฉ
updateGroup WHERE ์ ˆ RTRIM ์ ์šฉ
deleteGroup WHERE ์ ˆ RTRIM ์ ์šฉ
countCodesByGroup WHERE ์ ˆ RTRIM ์ ์šฉ

INSERT๋Š” ์ˆ˜์ • ๋ถˆํ•„์š”: INSERT ์‹œ Oracle์ด ์ž๋™์œผ๋กœ CHAR ๊ธธ์ด์— ๋งž๊ฒŒ ํŒจ๋”ฉํ•˜๋ฏ€๋กœ ์ •์ƒ ๋™์ž‘ํ•œ๋‹ค.


5. ์žฌ๋ฐœ ๋ฐฉ์ง€ ๋Œ€์ฑ…

5.1 DB ์Šคํ‚ค๋งˆ ์„ค๊ณ„ ์›์น™ (์‹ ๊ทœ ํ…Œ์ด๋ธ”)

CHAR ํƒ€์ž…์€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ณ ์ • ๊ธธ์ด ์ฝ”๋“œ์„ฑ ์ปฌ๋Ÿผ๋„ VARCHAR2๋กœ ์„ ์–ธํ•œ๋‹ค.


-- โŒ ๊ธˆ์ง€

GROUP_CD  CHAR(5)

  

-- โœ… ๊ถŒ์žฅ

GROUP_CD  VARCHAR2(5)  NOT NULL

CHAR๋Š” ๋น„๊ต ์‹œ ํƒ€์ž…์— ๋”ฐ๋ผ ๋™์ž‘์ด ๋‹ฌ๋ผ์ง€๋Š” ์ˆจ๊ฒจ์ง„ ์œ„ํ—˜์ด ์žˆ๋‹ค. Oracle์—์„œ VARCHAR2๋Š” ์ž…๋ ฅ ๊ธธ์ด ๊ทธ๋Œ€๋กœ ์ €์žฅํ•˜๋ฉฐ JDBC์™€ ์™„์ „ํžˆ ํ˜ธํ™˜๋œ๋‹ค.

5.2 ๊ธฐ์กด CHAR ์ปฌ๋Ÿผ์„ ๋‹ค๋ฃจ๋Š” MyBatis ์ž‘์„ฑ ๊ทœ์น™

๋ ˆ๊ฑฐ์‹œ DB์˜ CHAR ์ปฌ๋Ÿผ์„ ํ”ผํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ, ์•„๋ž˜ ๊ทœ์น™์„ ๋ฐ˜๋“œ์‹œ ๋”ฐ๋ฅธ๋‹ค.


<!-- โŒ ๊ธˆ์ง€: CHAR ์ปฌ๋Ÿผ์„ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์ง์ ‘ ๋น„๊ต -->

WHERE GROUP_CD = #{groupCd}

  

<!-- โœ… ํ•„์ˆ˜: RTRIM์œผ๋กœ ๊ณต๋ฐฑ ์ œ๊ฑฐ ํ›„ ๋น„๊ต -->

WHERE RTRIM(GROUP_CD) = #{groupCd}


<!-- โŒ ๊ธˆ์ง€: CHAR ์ปฌ๋Ÿผ์„ ๊ทธ๋Œ€๋กœ SELECT -->

SELECT GROUP_CD FROM T_CODEGROUP

  

<!-- โœ… ๊ถŒ์žฅ: RTRIM์œผ๋กœ ์ •๊ทœํ™”ํ•˜์—ฌ SELECT -->

SELECT RTRIM(GROUP_CD) AS GROUP_CD FROM T_CODEGROUP

5.3 ์ฝ”๋“œ ๋ฆฌ๋ทฐ ์ฒดํฌ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ ์ถ”๊ฐ€

PR ๋ฆฌ๋ทฐ ์‹œ ์•„๋ž˜ ํ•ญ๋ชฉ์„ ์ ๊ฒ€ํ•œ๋‹ค.


[ ] Oracle CHAR ํƒ€์ž… ์ปฌ๋Ÿผ๊ณผ์˜ WHERE ๋น„๊ต์— RTRIM์ด ์ ์šฉ๋˜์–ด ์žˆ๋Š”๊ฐ€?

[ ] CHAR ํƒ€์ž… ์ปฌ๋Ÿผ์ด SELECT์— ํฌํ•จ๋œ ๊ฒฝ์šฐ RTRIM ์ •๊ทœํ™”๊ฐ€ ๋˜์–ด ์žˆ๋Š”๊ฐ€?

[ ] ์‹ ๊ทœ ํ…Œ์ด๋ธ” DDL์— CHAR ํƒ€์ž…์ด ์‚ฌ์šฉ๋˜์ง€ ์•Š์•˜๋Š”๊ฐ€?

5.4 SQL Developer์™€ JDBC ๋™์ž‘ ์ฐจ์ด ์ธ์ง€

ํ…Œ์ŠคํŠธ ์‹œ ๋‹ค์Œ ์‚ฌํ•ญ์„ ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•œ๋‹ค.

์ ๊ฒ€ ํ•ญ๋ชฉ ๋ฐฉ๋ฒ•
P6SPY ๋กœ๊ทธ๋งŒ์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ํŒ๋‹จํ•˜์ง€ ์•Š๋Š”๋‹ค P6SPY๋Š” ๋ฐ”์ธ๋”ฉ ๊ฐ’์„ ๋ณด์—ฌ์ฃผ์ง€๋งŒ, ํƒ€์ž… ์ •๋ณด๋Š” ํ‘œ์‹œํ•˜์ง€ ์•Š๋Š”๋‹ค
SQL Developer ์ง์ ‘ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๋งน์‹ ํ•˜์ง€ ์•Š๋Š”๋‹ค ๋ฆฌํ„ฐ๋Ÿด ๋ฌธ์ž์—ด์€ CHAR ๋น„๊ต, JDBC๋Š” VARCHAR2 ๋น„๊ต๋กœ ๋™์ž‘์ด ๋‹ค๋ฅด๋‹ค
๋‹จ๊ฑด ์กฐํšŒ๋Š” ๋ชฉ๋ก ์กฐํšŒ์™€ ๋ณ„๋„๋กœ ๋ฐ˜๋“œ์‹œ ํ…Œ์ŠคํŠธํ•œ๋‹ค WHERE ์ ˆ์ด ์—†๋Š” ๋ชฉ๋ก๊ณผ ๋‹ฌ๋ฆฌ, ๋‹จ๊ฑด์€ CHAR ๋น„๊ต ๋ฌธ์ œ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค

5.5 ๋””๋ฒ„๊น… ์‹œ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ธธ์ด ์ถœ๋ ฅ ํŒจํ„ด

CHAR ํƒ€์ž… ๋ฌธ์ œ๋Š” ๊ฐ’์ด ๊ฐ™์•„ ๋ณด์—ฌ๋„ ๊ธธ์ด๊ฐ€ ๋‹ค๋ฅด๋ฉด ๋ฐœ์ƒํ•œ๋‹ค. ์˜์‹ฌ ์‹œ ๊ธธ์ด๋ฅผ ํ•จ๊ป˜ ๋กœ๊น…ํ•œ๋‹ค.


log.debug("groupCd='{}' len={}", groupCd, groupCd == null ? -1 : groupCd.length());

DB ์‹ค์ œ๊ฐ’๊ณผ ์ˆ˜์‹  ํŒŒ๋ผ๋ฏธํ„ฐ์˜ len์ด ๋‹ค๋ฅด๋ฉด CHAR ํŒจ๋”ฉ ๋ฌธ์ œ๋ฅผ ์˜์‹ฌํ•œ๋‹ค.


6. Oracle CHAR vs VARCHAR2 ํ•ต์‹ฌ ์ •๋ฆฌ

ํ•ญ๋ชฉ CHAR(n) VARCHAR2(n)
์ €์žฅ ๋ฐฉ์‹ ๊ณ ์ • ๊ธธ์ด (๊ณต๋ฐฑ ํŒจ๋”ฉ) ๊ฐ€๋ณ€ ๊ธธ์ด (์ž…๋ ฅ๊ฐ’ ๊ทธ๋Œ€๋กœ)
= CHAR ๋น„๊ต ๊ณต๋ฐฑ ํŒจ๋”ฉ ๋น„๊ต (blank-padded) โ€”
= VARCHAR2 ๋น„๊ต ๋น„ํŒจ๋”ฉ ๋น„๊ต (non-padded) ๋น„ํŒจ๋”ฉ ๋น„๊ต
JDBC String ๋น„๊ต ๋น„ํŒจ๋”ฉ โ†’ '002' โ‰  '002 ' ๋น„ํŒจ๋”ฉ โ†’ '002' = '002'
SQL Developer ๋ฆฌํ„ฐ๋Ÿด ๋น„๊ต ๊ณต๋ฐฑ ํŒจ๋”ฉ โ†’ '002' = '002 ' โ€”
ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• RTRIM(์ปฌ๋Ÿผ) = #{param} ๋ณ„๋„ ์ฒ˜๋ฆฌ ๋ถˆํ•„์š”

ํ•ต์‹ฌ: SQL Developer์—์„œ๋Š” ๋˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์•ˆ ๋œ๋‹ค๋ฉด CHAR vs VARCHAR2 ๋น„๊ต ๊ทœ์น™ ์ฐจ์ด๋ฅผ ๊ฐ€์žฅ ๋จผ์ € ์˜์‹ฌํ•˜๋ผ.