Curl Up Black Cat

๐Ÿ˜Ž ์„œ๋ก 

์•ˆ๋…•ํ•˜์„ธ์š”! ์˜ค๋Š˜์€ React Hook Form ๊ณผ useRef ์˜ ์กฐํ•ฉ์„ ํ†ตํ•ด ํŠน์ • input ์š”์†Œ์— ์ง์ ‘ ์ „๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ณต์œ ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.
์ตœ๊ทผ ๊ฒŒ์‹œํŒ ํ”„๋กœ์ ํŠธ์—์„œ ๊ฒŒ์‹œ๊ธ€์„ ์—ด ๋•Œ ๋Œ“๊ธ€ ์ž‘์„ฑ์ž ํ•„๋“œ์— ์ž๋™์œผ๋กœ ํฌ์ปค์Šค๋ฅผ ์ฃผ๊ณ  ์‹ถ์—ˆ๋Š”๋ฐ React Hook Form ๋„์ž… ์‹œ์— ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ์–ด๋ ค์›€์„ ๊ฒช์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ๋‚ด์šฉ๊ณผ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿค” ๋ฌธ์ œ์ƒํ™ฉ ์™œ ref={commentAuthorInputRef} ๋กœ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š์„๊นŒ?

React Hook Form ์„ ์‚ฌ์šฉํ•  ๋•Œ, ์ฃผ์š” ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” register ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” input ํ•„๋“œ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋‚˜ ํผ ์ œ์ถœ ๋“ฑ์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ œ๊ฐ€ ์ฒ˜์Œ์— ์‚ฌ์šฉํ•œ ๋ฐฉ์‹์€ ์•„๋ž˜์™€ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค.

const commentAuthorInputRef = useRef(null)

...

useEffect(() => {
    commentAuthorInputRef.current.focus();
}, []);

...

<input ref={commentAuthorInputRef} ... />

๊ทธ๋Ÿฐ๋ฐ, ์œ„์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด React Hook Form ์˜ register ์™€ ref ์™€ ๋™์‹œ์— ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์•„, ์ž…๋ ฅ ๊ฐ’์ด undefined ๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
 
register ํ•จ์ˆ˜๋Š” input ํ•„๋“œ๋ฅผ ๋“ฑ๋กํ•˜๋ฉด์„œ ํ•ด๋‹น  ํ•„๋“œ์˜ ์ฐธ์กฐ ref ๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, ์™ธ๋ถ€์—์„œ ๋ณ„๋„์˜ ref ๋ฅผ ์—ฐ๊ฒฐํ•  ๋•Œ๋Š”  register ์™€์˜ ์—ฐ๋™์ด ์ค‘์ฒฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋•Œ๋ฌธ์—, ๋‚ด๋ถ€์ ์œผ๋กœ ํผ ๊ฐ’์˜ ์ƒํƒœ ๊ด€๋ฆฌ๋‚˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ๋“ค์ด ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


๐ŸŒŸ ํ•ด๊ฒฝ ๋ฐฉ๋ฒ•: ref ๋ฅผ ํ•จ์ˆ˜๋กœ ์ •์˜ํ•˜๊ธฐ

React Hook Form ์˜ register ํ•จ์ˆ˜๋Š” ref ์™€ ํ•จ๊ป˜ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์ž…๋ ฅ ์š”์†Œ์— ์—ฐ๊ฒฐํ•˜๋ฉด์„œ ๋™์‹œ์— ์™ธ๋ถ€ ref ์—๋„ ์—ฐ๊ฒฐํ•ด์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 <input
  {...commentRegister('author', { 
  	required: '๋Œ“๊ธ€ ์ž‘์„ฑ์ž๋Š” ํ•„์ˆ˜ ์ž…๋ ฅ์ž…๋‹ˆ๋‹ค.' 
  })}
  ref={(e) => {
    commentRegister('author').ref(e); // react-hook-form ์„ ์œ„ํ•œ ref
    commentAuthorInputRef.current = e; // ์™ธ๋ถ€ ref
  }}
  ...
/>

์œ„ ์ฝ”๋“œ์—์„œ ์ค‘์š”ํ•œ ์ ์€ ref ํ•จ์ˆ˜๊ฐ€ ๋‘ ๊ฐ€์ง€ ์—ฐ๊ฒฐ ์ž‘์—…์„ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
1. React Hook Form ์˜ ref ์— ์š”์†Œ(HTMLInputElement)๋ฅผ ์—ฐ๊ฒฐ
2. ์™ธ๋ถ€ ref์—๋„ ๋™์ผํ•œ ์š”์†Œ(HTMLInputElement)๋ฅผ ์—ฐ๊ฒฐ
 
์ด๋ฅผ ํ†ตํ•ด React Hook Form ์˜ ๊ธฐ๋Šฅ์„ ๊ทธ๋Œ€๋กœ ํ™œ์šฉํ•˜๋ฉด์„œ๋„, ์™ธ๋ถ€์—์„œ๋„ input ์š”์†Œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.๐Ÿคญ


๐ŸŽฌ  ๊ฒฐ๋ก 

React Hook Form ์€ ๋งค์šฐ ํŽธ๋ฆฌํ•œ ๋„๊ตฌ์ด์ง€๋งŒ, ๋•Œ๋•Œ๋กœ ๋‹ค๋ฅธ React ๊ธฐ๋Šฅ๊ณผ์˜ ์กฐํ•ฉ์ด ํ•„์š”ํ•  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ค๋Š˜ ์†Œ๊ฐœํ•œ ๋ฐฉ์‹์€ ๊ทธ๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋„ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํŠนํžˆ, ์™ธ๋ถ€ ref์™€ ํ•จ๊ป˜ React Hook Form ์„ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•  ๋•Œ ์ด ๋ฐฉ์‹์ด ํฐ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. 
 
๊ทธ๋Ÿผ ์˜ค๋Š˜๋„ ์ฆ๊ฒ๊ฒŒ ์ฝ”๋”ฉํ•˜์„ธ์š”! ๐Ÿ˜
 


๐Ÿ˜Ž ์„œ๋ก 

์˜ค๋Š˜์€ ๋ฆฌ์•กํŠธ์—์„œ ๋™์  ๋ผ์šฐํŒ…์„ ์‚ฌ์šฉํ•˜์—ฌ ํŽ˜์ด์ง€ ๋ชจ๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ฒŒ์‹œํŒ ํ™”๋ฉด์„ ๋งŒ๋“ค์–ด ๋ณด๋ฉด์„œ ๋“ฑ๋ก, ์ˆ˜์ •, ์ƒ์„ธ์˜ ํ™”๋ฉด์„ ๋‹จ ํ•˜๋‚˜์˜ ํŽ˜์ด์ง€์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ์˜€์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๋ฆฌ์•กํŠธ๋กœ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ํŽ˜์ด์ง€ ๊ฐ„์˜ ์ด๋™ ๋ฐ ๋ฐ์ดํ„ฐ์˜ ํ™œ์šฉ์„ ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํšจ๊ณผ์ ์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„๊นŒ? ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€ ๋™์  ๋ผ์šฐํŒ… ํ™œ์šฉ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ˜ƒ ํŽ˜์ด์ง€ ๋ชจ๋“œ์˜ ์ •์˜

๋จผ์ €, AppTypes ํŒŒ์ผ์—์„œ ํŽ˜์ด์ง€์˜ ๋ชจ๋“œ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

const AppTypes = {
  PageMode: {
    view: 'view',
    add: 'add',
    edit: 'edit',
  },
  ...
}

ํŽ˜์ด์ง€์˜ ๋ชจ๋“œ๋Š” view, add, edit ์„ธ ๊ฐ€์ง€๋กœ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค. ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ App ์—์„œ ํ™œ์šฉ๋œ๋Š ๊ฐ ๋ชจ๋“œ ํƒ€์ž…์„ ๋ณ„๋„์˜ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๋ฉด ํ”„๋กœ์ ํŠธ์˜ ๊ตฌ์กฐ๊ฐ€ ๋”์šฑ ๋ช…ํ™•ํ•ด์ง€๊ณ  ์œ ์ง€ ๋ณด์ˆ˜๊ฐ€ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.


๐ŸŒŸ ๋ฆฌ์•กํŠธ ๋ผ์šฐํ„ฐ์˜ ํ™œ์šฉ

๋ฆฌ์•กํŠธ์—์„œ ํŽ˜์ด์ง€๊ฐ„์˜ ์ด๋™์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋Š” ๋ฆฌ์•กํŠธ ๋ผ์šฐํ„ฐ๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํŠนํžˆ ๋™์  ๋ผ์šฐํŒ…์€ ๋™์ผํ•œ UI ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๋ฉด์„œ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ๋ชจ๋“œ๋ฅผ ํ™œ์šฉํ•ด ๋ผ์šฐํ„ฐ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฒฝ๋กœ์™€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—ฐ๊ฒฐํ•ด ์ฃผ์—ˆ๋Š”๋ฐ์š”..

import { Navigate, Route, Routes } from 'react-router-dom'

const App = () => {
  return (
    <Routes>
      <Route path="/board/list" element={<LayoutWithComponent layout={EmptyLayout} component={Board} />} />
      <Route path="/board/view/:id" element={<LayoutWithComponent layout={EmptyLayout} component={BoardView} />} />
      <Route path="/board/:mode" element={<LayoutWithComponent layout={EmptyLayout} component={BoardView} />} />
      ...
    </Routes>
  )
}

/board/:mode ๊ฒฝ๋กœ์™€ ๊ฐ™์ด ๋™์  ๋ผ์šฐํŠธ ๋ฐฉ์‹์œผ๋กœ ๊ฒŒ์‹œํŒ์˜ ๋‹ค์–‘ํ•œ ๋ชจ๋“œ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๋ฉด ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ์˜ ๋ณต์žก๋„๋ฅผ ์ค„์ผ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋™์  ๋ผ์šฐํŒ…์œผ๋กœ ํŽ˜์ด์ง€๋ฅผ ๋„˜๊ธฐ๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์ฝ”๋“œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

const handleAddClick = useCallback(() => {
  navigateTo(`/board/${AppTypes.PageMode.add}`)
}, [navigateTo])

const handleRowClick = useCallback((id) => {
  navigateTo(`/board/view/${id}`)
}, [navigateTo])

๋‹ค์–‘ํ•œ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์œ ์—ฐํ•˜๊ฒŒ URL์„ ๊ตฌ์„ฑํ•˜์—ฌ ํ•ด๋‹น URL ์— ๋งž๋Š” ํŽ˜์ด์ง€๋กœ ๋ผ์šฐํŒ… ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“‘ ํŽ˜์ด์ง€ ๋ชจ๋“œ ๊ด€๋ฆฌ

View ์—์„œ๋Š” ๊ฒŒ์‹œ๊ธ€์˜ ์ƒ์„ธ ๋‚ด์šฉ์„ ๋ณด์—ฌ์ฃผ๋ฉฐ, ๊ฐ๊ฐ์˜ ๋ชจ๋“œ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์•ก์…˜ ๋ฒ„ํŠผ๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. useParams๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ์šฐํŠธ์—์„œ ํ•„์š”ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ ธ์™€ ์ƒํƒœ๋ฅผ ์ง€์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

const BorderView = () => {
  const { mode, id } = useParams() // mode: AppTypes.PageMode
  const [currentMode, setCurrentMode] = useState(mode || AppTypes.PageMode.view)
...
}

์ด๋ ‡๊ฒŒ ํ•  ๊ฒฝ์šฐ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง์„ ํ™œ์šฉํ•˜์—ฌ ํ˜„์žฌ ๋ชจ๋“œ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ UI ์š”์†Œ๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{currentMode === AppTypes.PageMode.view && (
  <>
    <button type="button" className="list-btn" onClick={handleListClick}>๋ชฉ๋ก์œผ๋กœ</button>
    <button type="button" className="edit-btn" onClick={handleEditClick}>ํŽธ์ง‘</button>
  </>
)}

{currentMode !== AppTypes.PageMode.view && (
  ...
)}

์ด ์™ธ์—๋„ ๊ฐ ๋ชจ๋“œ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๋Š” ๋“ฑ์˜ ๋™์ž‘์„ ๋ถ„๊ธฐ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const onSubmit = useCallback(async (data) => {
  const payload = {
    ...data
  }
  
  try {
    if (currentMode === AppTypes.PageMode.add) {
      await BoardService.create(payload)
      navigateTo('/board')
    }
  
    if (currentMode === AppTypes.PageMode.edit) {
      await BoardService.update(id, payload)
      navigateBack()
    }
  } catch (err) {
    handleError(err.response)
  }
}, [currentMode, id, navigateTo, navigateBack, handleError])

๐Ÿ”„ ํŽ˜์ด์ง€ ๋ชจ๋“œ ๋ณ€๊ฒฝํ•˜๊ธฐ

useState ์— ์„ ์–ธํ•œ setCurrentMode ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง€์˜ ๋™์ž‘ ๋ชจ๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž์˜ ์•ก์…˜(์˜ˆ: ํŽธ์ง‘ ๋ฒ„ํŠผ ํด๋ฆญ)์— ๋”ฐ๋ผ ๋™์ž‘ ๋ชจ๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ , ๊ทธ์— ๋”ฐ๋ฅธ UI๋ฅผ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const handleEditClick = useCallback((e) => {
  e.preventDefault()
  setCurrentMode(AppTypes.PageMode.edit)
}, [])

๐ŸŽฌ ๊ฒฐ๋ก 

๋ฆฌ์—‘ํŠธ ๋ผ์šฐํ„ฐ์˜ ๋™์  ๋ผ์šฐํŒ…์„ ํ™œ์šฉํ•˜๋ฉด, ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์— ๋”ฐ๋ผ ํŽ˜์ด์ง€์˜ ๋‚ด์šฉ๊ณผ ์ƒํƒœ๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ๋ชจ๋“œ์˜ ๊ด€๋ฆฌ์™€ ๋ผ์šฐํŒ…์„ ์ตœ์ ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ดค๋Š”๋ฐ, ๋ฆฌ์—‘ํŠธ ํ”„๋กœ์ ํŠธ์—์„œ ์ด ๊ธฐ๋ฒ•์„ ์ ์šฉํ•˜๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์€ ๋ฌผ๋ก , ๊ฐœ๋ฐœ์ž์˜ ์ž‘์—… ํšจ์œจ๋„ ์ƒ๋‹นํžˆ ์ฆ์ง„๋  ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๐Ÿ™‚


๐Ÿ˜Ž ์„œ๋ก  

์•ˆ๋…•ํ•˜์„ธ์š” ์˜ค๋Š˜์€ Spring์—์„œ ์ž์ฃผ ๋งˆ์ฃผ์น˜๋Š” ๋ฌธ์ œ ์ค‘ ํ•˜๋‚˜์ธ API ์ž˜๋ชป๋œ ๋ฉ”์‹œ์ง€ ์š”์ฒญ์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ๋ฅผ ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ–ˆ๋Š”์ง€ ์ž‘์„ฑํ•ด ๋ณผ๊ฒŒ์š”.

 

๊ฒŒ์‹œํŒ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๋ฉด์„œ ๋ฐฑ์—”๋“œ์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ API ์š”์ฒญ์„ ๋ฐ›๊ฒŒ ๋˜๋ฉด, ์š”์ฒญ์˜ JSON ํฌ๋งท์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ค‘์—์„œ๋„ ์•Œ ์ˆ˜ ์—†๋Š” ํ•„๋“œ๋ฅผ ์ „๋‹ฌ ๋ฐ›์•˜์„ ๋•Œ ์ด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐ ํ•  ์ˆ˜ ์žˆ์„๊นŒ ๊ณ ๋ฏผ์„ ํ–ˆ๊ณ ..


๐Ÿค” HttpMessageNotReadableException ์ฒ˜๋ฆฌ

Spring ์—์„œ ์ œ๊ณตํ•˜๋Š” @ExceptionHandler ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ HttpMessageNotReadableException ์„ ์ฒ˜๋ฆฌํ•˜๋ฉด์„œ, ๊ทธ ์›์ธ์ด ๋˜๋Š” cause ๋ฅผ ํ†ตํ•ด ๋” ๊ตฌ์ฒด์ ์ธ ์˜ˆ์™ธ ์ƒํ™ฉ์„ ํŒŒ์•…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ฒŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

@ExceptionHandler(value = {HttpMessageNotReadableException.class})
public ResponseEntity handleJsonParseException(HttpMessageNotReadableException ex) {
    final Throwable cause = ex.getCause();
    ...
}

 

์ด ๋•Œ, ๋ฐœ๊ฒฌํ•œ ๊ฒƒ์ด UnrecognizedPropertyException ์ด๋ผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ์˜ˆ์™ธ๋Š” JSON์˜ ์•Œ ์ˆ˜ ์—†๋Š” ํ•„๋“œ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•˜๋ฏ€๋กœ, ํ•ด๋‹น ํ•„๋“œ์˜ ์ด๋ฆ„์„ ์–ป์–ด์™€ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋” ๊ตฌ์ฒด์ ์ธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค.

if (cause instanceof UnrecognizedPropertyException) {
    final UnrecognizedPropertyException unrecognizedPropertyException = (UnrecognizedPropertyException) cause;
    final String fieldName = unrecognizedPropertyException.getPropertyName();
    ...
}

 

๊ทธ๋ฆฌ๊ณ  ์ด๋ ‡๊ฒŒ ์–ป์–ด์˜จ ํ•„๋“œ ์ด๋ฆ„์„ ErrorCode.UNKNOWN_FIELD ์™€ ํ•จ๊ป˜ ๋ฐ˜ํ™˜ํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ํ•ด๋‹น ํ•„๋“œ๊ฐ€ ๋ฌธ์ œ๋ผ๋Š” ๊ฒƒ์„ ์‘๋‹ตํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋ฉด, ํด๋ผ์ด์–ธํŠธ๋Š” ์–ด๋–ค ํ•„๋“œ ๋•Œ๋ฌธ์— ์š”์ฒญ์ด ์‹คํŒจํ–ˆ๋Š”์ง€ ์‰ฝ๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.๐Ÿ˜€

 

์ „์ฒด ์†Œ์Šค๋ฅผ ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด @ExceptionHandler ๋ฅผ ๊ตฌ์ถ•ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

@ExceptionHandler(value = {HttpMessageNotReadableException.class})
public ResponseEntity handleJsonParseException(HttpMessageNotReadableException ex) {
    final Throwable cause = ex.getCause();

    if (cause instanceof UnrecognizedPropertyException) {
        final UnrecognizedPropertyException unrecognizedPropertyException = (UnrecognizedPropertyException) cause;
        final String fieldName = unrecognizedPropertyException.getPropertyName();
        final String errorMessage = String.format(ErrorCode.UNKNOWN_FIELD.getMessage() + " : '%s'", fieldName);
        final ResponseModel responseModel = ResponseModel.failure(ErrorCode.UNKNOWN_FIELD, errorMessage);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseModel);
    }

    // ๊ธฐ๋ณธ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
    final ResponseModel responseModel = ResponseModel.failure(ErrorCode.INVALID_JSON, ex.getMessage());
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseModel);
}

 

๊ทธ ๋ฐ–์— JSON ๋ฌธ๋ฒ• ์˜ค๋ฅ˜ / ํƒ€์ž… ๋ถˆ์ผ์น˜ / JSON ๊ตฌ์กฐ์˜ ๋ถˆ์ผ์น˜ / ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ๋Š” ๊ธฐ๋ณธ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ๋กœ ์‘๋‹ตํ•˜๋Š” INVALID_JSON ์œผ๋กœ ์‘๋‹ตํ•˜๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


๐Ÿค  Response ์‘๋‹ต ๋ฉ”์‹œ์ง€

๊ตฌํ˜„๋œ ํ•ธ๋“ค๋Ÿฌ์˜ ์‘๋‹ต๋ฉ”์‹œ์ง€๋Š” ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.๐Ÿ™‚

 

์ž˜๋ชป๋œ ํ•„๋“œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์ด ๋˜์—ˆ์„ ๊ฒฝ์šฐ..

UnrecognizedPropertyException ์˜ˆ์™ธ

 

JSON ๋ฌธ๋ฒ•์˜ค๋ฅ˜ ๋“ฑ ๊ธฐ๋ณธ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ์ด ๋˜์—ˆ์„ ๊ฒฝ์šฐ..

๊ทธ ๋ฐ–์˜ HttpMessageNotReadableException ์˜ˆ์™ธ


๐Ÿฅธ ๊ฒฐ๋ก 

์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” ๋‹จ์ˆœํžˆ ์˜ค๋ฅ˜๋ฅผ  ํฌ์ฐฉํ•˜๋Š” ๊ฒƒ์„ ๋„˜์–ด ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•˜๋Š”๊ฑฐ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž์™€์˜ ์›ํ™œํ•œ ์†Œํ†ต๊ณผ ํ˜‘์—…์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ, ์ด๋กœ ์ธํ•ด ๋ณด๋‹ค ๋‚˜์€ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ฒŒ์‹œํŒ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๋ฉด์„œ ์ด์— ๋Œ€ํ•œ ์—ฐ๊ตฌ์™€ ๊ณ ๋ฏผ์„ ์ง€์†์ ์œผ๋กœ ํ•ด ๋‚˜๊ฐˆ ๊ณ„ํš์ž…๋‹ˆ๋‹ค.๐Ÿ™‚

 


๐Ÿ˜Ž ์„œ๋ก 

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค๋ณด๋ฉด ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ผ์šฐํŒ… ๊ด€๋ จ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด React Router ์˜ useNavigate ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ณด๋‹ค ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” custom hook ์„ ๋งŒ๋“ค์–ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

import { useNavigate } from 'react-router-dom'

const useAppNavigate = () => {
  const navigate = useNavigate()

  const navigateBack = () => {
    navigate(-1)
  }

  const navigateTo = (path) => {
    navigate(path)
  }

  return { navigateBack, navigateTo }
}

export default useAppNavigate

์—ฌ๊ธฐ์„œ useAppNavigate ๋ผ๋Š” custom hook ์€ ๋‘๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

 

1. navigateBack: ํ˜„์žฌ ํŽ˜์ด์ง€์—์„œ ์ด์ „ ํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐ€๋Š” ๊ธฐ๋Šฅ

2. navigateTo: ์›ํ•˜๋Š” ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•˜๋Š” ๊ธฐ๋Šฅ

 

์ด์ œ ์ด custom hook์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ๊ฐ„๋žตํ•˜๊ฒŒ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๐Ÿค  navigateBack ํ™œ์šฉ

๋•Œ๋ก  ์‚ฌ์šฉ์ž์—๊ฒŒ '๋’ค๋กœ๊ฐ€๊ธฐ' ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ navigateBack ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const { navigateBack } = useAppNavigate();

// '๋’ค๋กœ๊ฐ€๊ธฐ' ๋ฒ„ํŠผ์ด ํด๋ฆญ๋˜์—ˆ์„ ๋•Œ ์‹คํ–‰
<button onClick={navigateBack}>๋’ค๋กœ ๊ฐ€๊ธฐ</button>

 

๐Ÿฅธ navigateTo ํ™œ์šฉ

ํŠน์ • ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•˜๊ณ ์ž ํ•  ๋•Œ navigateTo ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

const { navigateTo } = useAppNavigate();

// '/profile' ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•˜๊ณ ์ž ํ•  ๋•Œ
<button onClick={() => navigateTo('/profile')}>ํ”„๋กœํ•„ ๋ณด๊ธฐ</button>

์š”์•ฝํ•˜๋ฉด React Router ์˜ useNavigate ๋Š” ๊ต‰์žฅํžˆ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ํŠน์ •ํ•œ ์‚ฌ์šฉ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์กฐ๊ธˆ ๋” ํ™•์žฅ๋œ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿด ๋•Œ ์œ„์™€ ๊ฐ™์€ custom hook ์„ ํ™œ์šฉํ•˜๋ฉด ๋ณด๋‹ค ํŽธ๋ฆฌํ•˜๊ฒŒ ๋ผ์šฐํŒ… ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๐Ÿค” ์™œ Custom Hooks ์„ ๋งŒ๋“ค์—ˆ๋Š”๊ฐ€?

useNavigate ์™€ ๊ฐ™์€ ๊ธฐ๋ณธ Hooks ๊ฐ€ ์žˆ๋‹ค๋ฉด, ์™œ ์ถ”๊ฐ€์ ์œผ๋กœ custom Hooks ๋ฅผ ๋งŒ๋“ค์—ˆ๋Š”์ง€ ๊ถ๊ธˆํ• ์ง€๋„ ๋ชจ๋ฆ…๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

1. ์ฝ”๋“œ ๊ฐ„๊ฒฐํ™”: ๋ฐ˜๋ณต๋˜๋Š” ๋ผ์šฐํŒ… ์ฝ”๋“œ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ๋ช…ํ™•์„ฑ: ํ•จ์ˆ˜์˜ ์ด๋ฆ„์„ ํ†ตํ•ด ์šฉ๋„๋ฅผ ๋ฐ”๋กœ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด, navigateBack ์€ ๋ฐ”๋กœ ๋’ค๋กœ ๊ฐ€๋Š” ๊ธฐ๋Šฅ์ž„์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ์œ ์—ฐ์„ฑ: ๋‚˜์ค‘์— ๋ผ์šฐํŒ… ๊ด€๋ จ ๋กœ์ง์„ ๋ณ€๊ฒฝํ•  ๋•Œ, ํ•œ ๊ณณ์—์„œ๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋˜๋ฏ€๋กœ ๊ด€๋ฆฌ๊ฐ€ ์‰ฝ์Šต๋‹ˆ๋‹ค.

4. ์žฌ์‚ฌ์šฉ: ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ™์€ ๋กœ์ง์„ ์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๐Ÿคญ ๊ฒฐ๋ก 

Custom Hooks ๋Š” ํ”„๋กœ์ ํŠธ์˜ ํšจ์œจ์„ฑ๊ณผ ๊ฐ€๋…์„ฑ์„ ๋†’์ด๋Š” ํ•ต์‹ฌ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ˜๋ณต๋˜๋Š” ์ฝ”๋“œ๋ฅผ ์ค„์ด๋ฉฐ, ๋ผ์šฐํŒ…๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๋” ๋ช…ํ™•ํ•˜๊ณ  ์ง๊ด€์ ์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ์˜ ๊ด€๋ฆฌ์™€ ์ˆ˜์ •์„ ์‰ฝ๊ฒŒ ํ•˜๋ฉฐ, ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค์ด ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๊ณ  ํ™œ์šฉํ•˜๋Š” ๋ฐ์—๋„ ํฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค.


๐Ÿ˜Ž ์„œ๋ก 

์•ˆ๋…•ํ•˜์„ธ์š”! ์˜ค๋Š˜์€ Spring Boot ์—์„œ API ์‘๋‹ต๊ณผ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ชจ๋ธ์„ ๊ตฌ์ถ•ํ•ด๋ดค๋˜ ๋‚ด์šฉ์„ ๊ณต์œ ํ•ด ๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ๋“ค๋„ ํ•˜๋‚˜ํ•˜๋‚˜ ๋ถ„์„ํ•ด ๋ณผ๊ป˜์š”. ์Šคํƒ€ํŠธ~!

 


1. ResponseModel - ์‘๋‹ต ๋ชจ๋ธ

๋จผ์ €, ๋ชจ๋“  API ์‘๋‹ต์— ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉ๋  ResponseModel ์„ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค.

@Getter
@Setter
@ToString
@AllArgsConstructor(staticName = "of")
public class ResponseModel<T> {
    private boolean success;
    private T data;
    private ErrorModel error;

    public static <T> ResponseModel<T> of(boolean success, T data) {
        return ResponseModel.of(success, data, null);
    }

    public static ResponseModel of(boolean success, ErrorCode code) {
        return ResponseModel.of(success, null, ErrorModel.of(code.name(), code.getMessage(), null));
    }

    public static ResponseModel of(boolean success, ErrorCode code, Exception ex) {
        return ResponseModel.of(success, null, code, ex);
    }

    public static <T> ResponseModel<T> of(boolean success, T data, ErrorCode code, Exception ex) {
        final ErrorModel error = (code != null) ? ErrorModel.of(code, ex) : null;
        return ResponseModel.of(success, data, error);
    }
}

์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ:

1. success: ์š”์ฒญ ์ฒ˜๋ฆฌ์˜ ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

2. data: ์š”์ฒญ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

3. error: ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๋Š” ErrorModel ๊ฐ์ฒด ์ž…๋‹ˆ๋‹ค.

 

ResponseModel ์—๋Š” ์—ฌ๋Ÿฌ static ์ƒ์„ฑ ๋ฉ”์„œ๋“œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋“ค์„ ํ†ตํ•ด ์„ฑ๊ณต ๋˜๋Š” ์˜ค๋ฅ˜ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์‰ฝ๊ฒŒ ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

2. ErrorModel - ์—๋Ÿฌ ๋ชจ๋ธ

ErrorModel ์€ ์˜ค๋ฅ˜์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ํ‘œ์ค€ํ™”ํ•˜์—ฌ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค.

 

package com.board.backend.model;

import com.board.backend.common.ErrorCode;
import com.board.backend.common.utils.ObjectUtil;
import com.board.backend.common.utils.StringUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@AllArgsConstructor(staticName = "of")
public class ErrorModel {
    private String code;
    private String message;
    private ErrorData data;

    @Getter
    @Setter
    @ToString
    @AllArgsConstructor(staticName = "of")
    public static class ErrorData {
        private String exceptionMessage;
        private String stackTrace;
    }

    public static ErrorModel of(ErrorCode code, Exception ex) {
        final String exceptionMessage = ObjectUtil.nonEmpty(ex) ? ex.getMessage() : null;
        final String stackTrace = ObjectUtil.nonEmpty(ex) ? getStackTraceAsString(ex) : null;
        final ErrorData data = (StringUtil.nonEmpty(exceptionMessage) || StringUtil.nonEmpty(stackTrace)) ? ErrorData.of(exceptionMessage, stackTrace) : null;
        return ErrorModel.of(code.name(), code.getMessage(), data);
    }

    private static String getStackTraceAsString(Exception ex) {
        final StringBuilder sb = new StringBuilder();
        for (StackTraceElement element : ex.getStackTrace()) {
            sb.append(element.toString());
            sb.append("\n");
        }
        return sb.toString();
    }
}

์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ:

1. code: ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

2. message: ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

3. data: ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ์˜ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ErrorData ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

 

ErrorData ํด๋ž˜์Šค๋Š” ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ์˜ ๋ฉ”์‹œ์ง€์™€ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค ์ •๋ณด๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์˜ค๋ฅ˜์˜ ์›์ธ์„ ๋”์šฑ ์ž์„ธํžˆ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.(์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋ž€? ํ”„๋กœ๊ทธ๋žจ์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ•ด๋‹น ์˜ˆ์™ธ์˜ ๋ฐœ์ƒ ๊ฒฝ๋กœ๋ฅผ ์ถ”์ ํ•˜๋Š” ์ •๋ณด์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์ˆœ์„œ์™€ ๋ผ์ธ ๋ฒˆํ˜ธ๋ฅผ ํฌํ•จํ•˜์—ฌ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์œ„์น˜์™€ ๊ทธ ์›์ธ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค๋‹ˆ๋‹ค.)

 

3. ErrorCode - ์—๋Ÿฌ ์ฝ”๋“œ

@Getter
@AllArgsConstructor
public enum ErrorCode {
    SERVER_ERROR("์„œ๋ฒ„ ์—๋Ÿฌ"),
    ARGUMENT_TYPE_MISMATCH("์ž˜๋ชป๋œ ํŒŒ๋ผ๋ฏธํ„ฐํƒ€์ž…์ด ์ „๋‹ฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."),
    BOARD_NOT_FOUND("๊ธ€์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค");
    ...
    private String message;
}

๊ฐ ์˜ค๋ฅ˜ ์œ ํ˜•์— ๋Œ€ํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ํ•จ๊ป˜ ์ œ๊ณตํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜๋ฏธ ์žˆ๋Š” ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌ ํ•ฉ๋‹ˆ๋‹ค. enum ํƒ€์ž…์œผ๋กœ ์ฝ”๋“œ์™€ ๋ฉ”์‹œ์ง€๋ฅผ ํ•จ๊ป˜ ๊ด€๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

4. ExceptionAdvice - ์ „์—ญ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๊ธฐ

Spring Boot ์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์˜ ์ค‘์š”์„ฑ์„ ๊ฐ•์กฐํ•˜๋ฉฐ, ์ด๋ฅผ ์œ„ํ•ด @ControllerAdvice ์™€ @ExceptionHandler ๋ฅผ ํ†ตํ•ด ํšจ๊ณผ์ ์ธ ์ „์—ญ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

@ControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(value = {Exception.class})
    public ResponseEntity handleException(Exception ex, WebRequest request) {
        final ResponseModel responseModel = ResponseModel.of(false, ErrorCode.SERVER_ERROR, ex);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(responseModel);
    }

    @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class})
    public ResponseEntity handleTypeException(Exception ex) {
        final ResponseModel<Object> responseModel = ResponseModel.of(false, ErrorCode.ARGUMENT_TYPE_MISMATCH,  ex);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseModel);
    }
    ...
}

 

1) @ControllerAdvice ์˜ ์—ญํ• 

@ControllerAdvice ๋Š” Spring ์—์„œ ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์™ธ๋“ค์„ ํ•œ๊ณณ์—์„œ ๊ด€๋ฆฌํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๋„์ž…๋œ ์–ด๋…ธํ…Œ์ด์…˜์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์ค‘๋ณต์„ ์ค„์ด๊ณ , ์ผ๊ด€๋œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ๋„์™€์ค๋‹ˆ๋‹ค. 

 

2) @ExceptionHandler 

@ExceptionHandler ๋Š” ํŠน์ • ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•œ Spring MVC ์˜ ์–ด๋…ธํ…Œ์ด์…˜์ž…๋‹ˆ๋‹ค. ๊ทธ ์ž์ฒด๋กœ๋„ ์œ ์šฉํ•˜์ง€๋งŒ, @ControllerAdvice์™€ ๊ฒฐํ•ฉํ•˜์—ฌ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์˜ ์˜ˆ์™ธ๋ฅผ ์ค‘์•™์—์„œ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

์˜ˆ์‹œ:

// ๋‹จ์ผ ์ง€์ •
@ExceptionHandler({CustomException.class})

// ๋‹ค์ค‘ ์ง€์ •
@ExceptionHandler({CustomException1.class, CustomException2.class, CustomException3.class})

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๋ฉด, ์œ„์—์„œ ์ง€์ •๋œ ์˜ˆ์™ธ๋“ค์ด ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

 

3) ํ™•์žฅ์„ฑ

@ControllerAdvice ์˜ ์žฅ์ ์ค‘ ํ•˜๋‚˜๋Š” ๊ทธ ํ™•์žฅ์„ฑ์ž…๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ์˜ˆ์™ธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์„ ์ˆ˜์ •ํ•ด์•ผ ํ•  ๋•Œ, @ExceptionHandler ๋ฅผ ์ ์ ˆํ•œ ๋ฉ”์„œ๋“œ์— ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•จ์œผ๋กœ์จ ์ค‘์•™์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ  ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿค  ๊ฒฐ๋ก 

Spring Boot ํ™˜๊ฒฝ์—์„œ ๋‚˜๋ฆ„๋Œ€๋กœ ์ƒ๊ฐํ•ด์„œ ๊ตฌํ˜„ํ–ˆ๋˜ ์‘๋‹ต๊ณผ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ผ๊ด€๋œ ์‘๋‹ต๊ณผ ์ •ํ™•ํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” API์˜ ์‹ ๋ขฐ์„ฑ์„ ๋†’์ด๋Š” ํ•ต์‹ฌ ์š”์†Œ๋กœ, ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž์™€์˜ ์‹ ๋ขฐ ๊ด€๊ณ„๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ , ๊ฐœ๋ฐœํŒ€ ๊ฐ„์˜ ํ˜‘์—… ํšจ์œจ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ™‚


๐Ÿ˜Ž ์„œ๋ก 

์•ˆ๋…•ํ•˜์„ธ์š”! ์˜ค๋Š˜์€ ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ JSON ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ์ ํ™”ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ๋ ค๋“œ๋ฆฌ๋ ค ํ•ฉ๋‹ˆ๋‹ค. ์›น ์„œ๋น„์Šค์—์„œ ์ž‘์€ ์ตœ์ ํ™”๋„ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์— ํฐ ๋ณ€ํ™”๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๊ธฐ์—, ์ด๋ฅผ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ๋งŽ์€ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 


๐Ÿค” ์™œ null ๊ฐ’์„ ์ œ๊ฑฐํ•ด์•ผ ํ•˜๋‚˜์š”?

JSON ์‘๋‹ต ๋ฐ์ดํ„ฐ์—์„œ null ๊ฐ’์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ์ ์„ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ฐ์ดํ„ฐํฌ๊ธฐ: ๋ถˆํ•„์š”ํ•œ null ๊ฐ’์€ ์ „์†ก๋˜๋Š” ๋ฐ์ดํ„ฐ์˜ ํฌ๊ธฐ๋ฅผ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์ฆ๊ฐ€์‹œํ‚ต๋‹ˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ์ฒ˜๋ฆฌ: ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” null ๊ฐ’์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

Before

{
    "id": 14,
    "title": "์ œ๋ชฉ",
    "content": "๋‚ด์šฉ",
    "writer": "ํ™๊ธธ๋™",
    "created_id": null
}

๐Ÿฅธ Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ Null ๊ฐ’ ์ œ๊ฑฐํ•˜๊ธฐ

Spring Boot์—์„œ๋Š” Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜์—ฌ JSON ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์˜ ์„ค์ •์„ ์‚ฌ์šฉํ•˜๋ฉด, null ๊ฐ’์„ ๊ฐ€์ง„ ํ•„๋“œ๋Š” JSON ์‘๋‹ต์—์„œ ์ž๋™์œผ๋กœ ์ œ์™ธ๋ฉ๋‹ˆ๋‹ค.

 

package com.board.backend.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public ObjectMapper objectMapper() {
        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return objectMapper;
    }
}

 

์ ์šฉ ํ›„์˜ ๊ฒฐ๊ณผ ์ž…๋‹ˆ๋‹ค.

 

After

{
    "id": 14,
    "title": "์ œ๋ชฉ",
    "content": "๋‚ด์šฉ",
    "writer": "ํ™๊ธธ๋™"
}

๐Ÿค  ๋งˆ์น˜๋ฉฐ

JSON ๋‚ด์˜ null ๊ฐ’์„ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์€ ๊ฐ„๋‹จํ•œ ์ ˆ์ฐจ์ฒ˜๋Ÿผ ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ด๋Ÿฌํ•œ ์ž‘์€ ์ตœ์ ํ™”๊ฐ€ ์„œ๋น„์Šค์˜ ์‘๋‹ต ์‹œ๊ฐ„ ๊ฐœ์„ ๊ณผ ํด๋ผ์ด์–ธํŠธ์˜ ์ฒ˜๋ฆฌ ๋ถ€๋‹ด ๊ฐ์†Œ์— ํฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค. ํŠนํžˆ, ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ์„œ๋น„์Šค๋‚˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ œํ•œ๋œ ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ์—์„œ๋Š” ์ด๋Ÿฐ ์ตœ์ ํ™”๊ฐ€ ๋”์šฑ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

 

๋ฐ์ดํ„ฐ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ , ์‚ฌ์šฉ์ž์—๊ฒŒ ํ•„์š”ํ•œ ์ •๋ณด๋งŒ์„ ํšจ์œจ์ ์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์€ ์ข‹์€ ์›น ์„œ๋น„์Šค์˜ ๊ธฐ๋ณธ์ž…๋‹ˆ๋‹ค. ์ด ๊ธ€์„ ํ†ตํ•ด ์†Œ๊ฐœ๋œ ๋ฐฉ๋ฒ•์ด ์—ฌ๋Ÿฌ๋ถ„์˜ ์„œ๋น„์Šค์— ์ ์šฉ๋˜์–ด, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ•œ ๋‹จ๊ณ„ ๋” ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ ๋„์›€์ด ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 


๐Ÿ˜Ž ์„œ๋ก 

์•ˆ๋…•ํ•˜์„ธ์š”, ์ธ๋”์ œ์ด ์ž…๋‹ˆ๋‹ค.

 

์˜ค๋Š˜์€ MyBatis XML Mapper์—์„œ Java์˜ ๋‚ด๋ถ€ ํด๋ž˜์Šค ์ฐธ์กฐ์‹œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์™€ ๊ทธ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค๋“œ๋ฆฌ๋ ค ํ•ฉ๋‹ˆ๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ ์ด ๋ฌธ์ œ๋กœ ์ธํ•ด ์ƒ๋‹นํ•œ ์‹œ๊ฐ„์„ ํ—ค๋งธ๊ธฐ์— ์ด ๊ฒฝํ—˜์„ ์—ฌ๋Ÿฌ๋ถ„๊ณผ ๊ณต์œ ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

 

๐Ÿฅฒ ๋ฌธ์ œ ์ƒํ™ฉ

๋จผ์ €, ์•„๋ž˜๋Š” Java ๋‚ด๋ถ€ ํด๋ž˜์Šค๋ฅผ ํฌํ•จํ•œ BoardDto ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค.

package com.board.backend.model;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BoardDto {

    @Getter
    @Setter
    public static class Response {
        private long id;
        private String title;
        private String content;
        private String writer;
        private long view_count;
        private Long created_id;
        private LocalDateTime created_dt;
        private LocalDateTime modified_dt;
    }
}

์ด ๋ชจ๋ธ์„ MyBatis XML Mapper์— resultType์œผ๋กœ ์„ค์ •ํ•˜๋ ค๊ณ  ํ–ˆ์„ ๋•Œ, ๋‚ด๋ถ€ ํด๋ž˜์Šค Response๋ฅผ ์ฐธ์กฐํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฒ˜์Œ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

<select id="findById" resultType="com.board.backend.model.BoardDto.Response">
    SELECT *
    FROM board
    WHERE id = #{id}
</select>

ํ•˜์ง€๋งŒ ์œ„์™€ ๊ฐ™์ด ์„ค์ •ํ–ˆ์„ ๋•Œ, ์—ฐ๊ฒฐ์ด ์ œ๋Œ€๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์•˜๊ณ , ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

Error creating bean with name 'boardMapper' defined in file  ...
Cannot resolve reference to bean 'sqlSessionTemplate' while setting bean property 'sqlSessionTemplate'

๐Ÿค” ์›์ธ 

sqlSessionTemplate ์—ฐ๊ฒฐ ์—๋Ÿฌ์˜ ์›์ธ์€ ๋‹ค์–‘ํ•ฉ๋‹ˆ๋‹ค: ์„ค์ • ํŒŒ์ผ ๋ˆ„๋ฝ, ์„ค์ • ํŒŒ์ผ ์œ„์น˜ ๋ถˆ์ผ์น˜, ์˜์กด์„ฑ ๋ฌธ์ œ, ๋ฐ์ดํ„ฐ ์†Œ์Šค ์—ฐ๊ฒฐ ๋ฌธ์ œ, ๋นˆ ์ด๋ฆ„ ๋ถˆ์ผ์น˜, ์ƒ์„ฑ์ž/์„ธํ„ฐ ์ฃผ์ž… ๋ฌธ์ œ ๋“ฑ. ์ด ๋ชจ๋“  ๊ฐ€๋Šฅ์„ฑ์„ ํ™•์ธํ–ˆ์ง€๋งŒ, ๋ฌธ์ œ์˜ ์›์ธ์„ ์ฐพ์ง€ ๋ชปํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋˜ ์ค‘, Java์—์„œ ๋‚ด๋ถ€ ํด๋ž˜์Šค๋ฅผ ์ฐธ์กฐํ•  ๋•Œ $ ๊ธฐํ˜ธ๋กœ ์—ฐ๊ฒฐ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ™‚ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

Mapper XML์—์„œ resultType์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด์„  $ ๊ธฐํ˜ธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

<select id="findById" resultType="com.board.backend.model.BoardDto$Response">
    SELECT *
    FROM board
    WHERE id = #{id}
</select>

ํ•ต์‹ฌ์€ BoardDto.Response ๋Œ€์‹  BoardDto$Response๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

๊ฒฐ๋ก ์ ์œผ๋กœ, MyBatis์—์„œ Java์˜ ๋‚ด๋ถ€ ํด๋ž˜์Šค๋ฅผ ์ฐธ์กฐํ•  ๋•Œ๋Š” $ ๊ธฐํ˜ธ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ €๋Š” ์ด ์ž‘์€ ๋ถ€๋ถ„ ๋•Œ๋ฌธ์— ๋งŽ์€ ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ•˜์˜€๋Š”๋ฐ ์ด ๊ธ€์„ ํ†ตํ•ด ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ๊ฒช๋Š” ๋ถ„๋“ค์—๊ฒŒ ๋„์›€์ด ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๐Ÿ˜Œ

Java Development Kit (JDK)์˜ ์—ฌ๋Ÿฌ ๋ฒ„์ „ ๊ฐ„์—๋Š” ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ, ๊ฐœ์„  ์‚ฌํ•ญ ๋ฐ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค. JDK 8, 11, 17 ๊ฐ„์˜ ์ฃผ์š” ์ฐจ์ด์ ์„ ์š”์•ฝํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

JDK 8

· ๋žŒ๋‹ค ํ‘œํ˜„์‹(Lambda Expressionis): ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ์ฃผ๋ฉฐ, ํŠนํžˆ ์ปฌ๋ ‰์…˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

· ์ŠคํŠธ๋ฆผ API(Stream API): ์ƒˆ๋กœ์šด ์ถ”์ƒํ™”๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์ž‘์—…์„ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.

· ๋‚ ์งœ์™€ ์‹œ๊ฐ„ API(Date and Time API): ๊ธฐ์กด์˜ java.util.Date ์™€ java.util.Calender ๋ฅผ ๋Œ€์ฒดํ•˜๋Š” ์ƒˆ๋กœ์šด API ์ž…๋‹ˆ๋‹ค.

· Default Methods: ์ธํ„ฐํŽ˜์ด์Šค์— ๋ฉ”์„œ๋“œ ๊ตฌํ˜„์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

· Nashorn JavaScript Engine: Java ์ฝ”๋“œ ๋‚ด์—์„œ JavaScript๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

 

JDK 11

· LTS(Long-Term Support) Release: ์ด ๋ฒ„์ „์€ ์˜ค๋žœ ๊ธฐ๊ฐ„ ๋™์•ˆ ์ง€์›๋ฉ๋‹ˆ๋‹ค.

· var ํ‚ค์›Œ๋“œ: ์ง€์—ญ ๋ณ€์ˆ˜์˜ ํƒ€์ž… ์ถ”๋ก ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์ค‘๋ณต์„ฑ์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

· String API์˜ ๊ฐœ์„ : ์ƒˆ๋กœ์šด ๋ฉ”์„œ๋“œ๋“ค(์˜ˆ: isBlank(), lines(), repeat(), strip())์ด ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

· HTTP ํด๋ผ์ด์–ธํŠธ(HTTP Client): java.net.http ํŒจํ‚ค์ง€๊ฐ€ ์ถ”๊ฐ€๋˜์–ด HTTP/2, ์›น์†Œ์ผ“ ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

· Java EE ๋ฐ CORBA ๋ชจ๋“ˆ ์ œ๊ฑฐ: Java EE์™€ CORBA ๊ด€๋ จ API ๋ฐ ๋„๊ตฌ๊ฐ€ JDK์—์„œ ์ œ๊ฑฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

JDK 17

· LTS(Long-Term Support) Release: JDK 11 ์ดํ›„์˜ ๋‹ค์Œ LTS ๋ฒ„์ „์ž…๋‹ˆ๋‹ค.

· Sealed Classes: ํด๋ž˜์Šค๋ฅผ sealํ•˜์—ฌ ํŠน์ • ์ž์‹ ํด๋ž˜์Šค๋งŒ ์ƒ๋ณต๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

· Pattern Matching for Switch(Preview): switch ๋ฌธ์„ ๊ฐœ์„ ํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

· JEP 356: Enhanced Pseudo-Random Number Generators: ์ƒˆ๋กœ์šด ์ธํ„ฐํŽ˜์ด์Šค ๋ฐ ๊ตฌํ˜„์„ ํ†ตํ•ด ๋‚œ์ˆ˜ ์ƒ์„ฑ์„ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.

· JEP 382: New macOS Rendering Pipeline: macOS์—์„œ์˜ ๊ทธ๋ž˜ํ”ฝ ๋ Œ๋”๋ง ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.

· JEP 411: Deprecate the Security Manager for Removal: Java์˜ ๋ณด์•ˆ ๊ด€๋ฆฌ์ž ๊ธฐ๋Šฅ์„ ๋น„์ถ”์ฒœ ์ƒํƒœ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

 

์ด์™ธ์—๋„ ๊ฐ ๋ฒ„์ „๋งˆ๋‹ค ์ˆ˜๋งŽ์€ ๊ธฐ๋Šฅ, ๊ฐœ์„  ์‚ฌํ•ญ, ๋ฒ„๊ทธ ์ˆ˜์ •, ์„ฑ๋Šฅ ํ–ฅ์ƒ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ๋ฒ„์ „์˜ JEP(JDK Enhancement-Proposal) ๋ชฉ๋ก์„ ํ™•์ธํ•˜๋ฉด ํ•ด๋‹น ๋ฒ„์ „์—์„œ ์–ด๋–ค ๋ณ€ํ™”๊ฐ€ ์žˆ์—ˆ๋Š”์ง€ ๋”์šด ์ž์„ธํžˆ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด๋ฒˆ ํฌ์ŠคํŒ…์€ "๋ฆฌ์•กํŠธ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ธฐ์ˆ " ์„œ์ ์„ ํ•™์Šตํ•˜๋ฉฐ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์™œ ๋ฆฌ์•กํŠธ์ธ๊ฐ€?

์ตœ๊ทผ ๋ช‡ ๋…„๊ฐ„ ์ „ ์„ธ๊ณ„ ๊ฐœ๋ฐœ์ž๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์— ๋œจ๋Ÿฝ๊ฒŒ ์—ด๊ด‘ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ํ˜„์žฌ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ฐ€์žฅ ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์–ธ์–ด๋งŒ์œผ๋กœ ๋ฐ์Šคํฌํ†ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, ๋ชจ๋ฐ”์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋“ฑ ๊ทœ๋ชจ๊ฐ€ ํฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ์‹œ๋Œ€์ž…๋‹ˆ๋‹ค. ๋Œ€๊ทœ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ค‘ ํ”„๋ŸฐํŠธ์—”๋“œ ์‚ฌ์ด๋“œ์—์„œ ๋Œ์•„๊ฐ€๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์กฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋ ค๋ฉด.. ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”?

์ด์— ํŽ˜์ด์Šค๋ถ ๊ฐœ๋ฐœํŒ€์€ ์•„์ด๋””์–ด๋ฅผ ๊ณ ์•ˆํ•ด ๋ƒˆ์Šต๋‹ˆ๋‹ค.

 

์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€ํ•  ๋•Œ๋งˆ๋‹ค ์–ด๋–ค ๋ณ€ํ™”๋ฅผ ์ค„์ง€ ๊ณ ๋ฏผํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ทธ๋ƒฅ ๊ธฐ์กด ๋ทฐ๋ฅผ ๋‚ ๋ ค ๋ฒ„๋ฆฌ๊ณ  ์ฒ˜์Œ๋ถ€ํ„ฐ ์ƒˆ๋กœ ๋ Œ๋”๋ง ํ•œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•˜๊ฒŒ๋˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์กฐ๊ฐ€ ๋งค์šฐ ๊ฐ„๋‹จํ•ด์ง€๊ณ , ์ž‘์„ฑํ•ด์•ผ ํ•  ์ฝ”๋“œ์–‘์ด ๋งŽ์ด ์ค„์–ด ๋“ค๋ฉฐ, ๋” ์ด์ƒ ์–ด๋–ป๊ฒŒ ๋ณ€ํ™”๋ฅผ ์ค„์ง€ ์‹ ๊ฒฝ ์“ธ ํ•„์š”๊ฐ€ ์—†๊ณ , ๊ทธ์ € ๋ทฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ƒ๊ธธ์ง€ ์„ ์–ธ๋งŒ ํ•˜๋ฉด ๋˜๋ฉฐ, ๋ฐ์ดํ„ฐ ๋ณ€ํ™”๊ฐ€ ์žˆ์œผ๋ฉด ๊ธฐ์กด์— ์žˆ๋˜ ๊ฒƒ์„ ๋ฒ„๋ฆฌ๊ณ  ์ƒˆ๋กœ ๋ Œ๋”๋ง ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

ํŽ˜์ด์Šค๋ถ ๊ฐœ๋ฐœ ํŒ€์€ ์ด๋Ÿฐ ๋ฐฉ์‹์„ ์ฑ„ํƒํ•˜์—ฌ ์ตœ๋Œ€ํ•œ ์„ฑ๋Šฅ์„ ์•„๋ผ๊ณ  ํŽธ์•ˆํ•œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(user experience)์„ ์ œ๊ณตํ•˜๋ฉด์„œ ๊ตฌํ˜„ํ•˜๊ณ ์ž ๊ฐœ๋ฐœํ•œ ๊ฒƒ์ด ๋ฆฌ์•กํŠธ(React) ์ž…๋‹ˆ๋‹ค.

 

๋ฆฌ์•กํŠธ์˜ ์ดํ•ด

์ด๋ฏธ์ง€์ถœ์ฒ˜: reactjs

๋ฆฌ์•กํŠธ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ์„œ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ ๊ตฌ์กฐ๊ฐ€ MVC, MVW ๋“ฑ์ธ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋‹ฌ๋ฆฌ ์˜ค์ง V(View)๋งŒ ์‹ ๊ฒฝ ์“ฐ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž…๋‹ˆ๋‹ค.

 

๋ฆฌ์•กํŠธ์—์„œ๋Š” ํŠน์ • ๋ถ€๋ถ„์ด ์–ด๋–ป๊ฒŒ ์ƒ๊ธธ์ง€ ์ •ํ•˜๋Š” ์„ ์–ธ์ฒด๊ฐ€ ์žˆ๋Š”๋ฐ ์ด๋ฅผ ์ปดํฌ๋„ŒํŠธ(component) ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๋Š” ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•œ API ๋กœ ์ˆ˜๋งŽ์€ ๊ธฐ๋Šฅ๋“ค์„ ๋‚ด์žฅํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์ปดํฌ๋„ŒํŠธ ํ•˜๋‚˜์—์„œ ์ปดํฌ๋„ŒํŠธ์˜ ์ƒ๊น€์ƒˆ์™€ ์ž‘๋™ ๋ฐฉ์‹์„ ์ •์˜ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๋˜ํ•œ ์‚ฌ์šฉ์ž ํ™”๋ฉด์— ๋ทฐ๋ฅผ ๋ณด์—ฌ ์ฃผ๋Š” ๊ฒƒ์„ ๋ Œ๋”๋ง์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

๋ฆฌ์•กํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋ทฐ๋ฅผ ์–ด๋–ป๊ฒŒ ๋ Œ๋”๋งํ•˜๊ธธ๋ž˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กญ๊ฒŒ ๋ฆฌ๋ Œ๋”๋ง ํ•˜๋ฉด์„œ ์„ฑ๋Šฅ์„ ์•„๋ผ๊ณ , ์ตœ์ ์˜ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณต ํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”? ์ด๋ฅผ ์ดํ•ดํ•˜๋ ค๋ฉด '์ดˆ๊ธฐ๋ Œ๋”๋ง'๊ณผ '๋ฆฌ๋ Œ๋”๋ง' ๊ฐœ๋…์„ ์ดํ•ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

1) ์ดˆ๊ธฐ ๋ Œ๋”๋ง

์ปดํฌ๋„ŒํŠธ์˜ render ํ•จ์ˆ˜๋ฅผ ํ†ตํ•˜์—ฌ ๋ Œ๋”๋ง์ด ์ด๋ฃจ์–ด์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ Œ๋”ํ•จ์ˆ˜๋Š” ๋ทฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๊ณ  ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ง€๋‹Œ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. render ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰ํ•˜๋ฉด ๋ Œ๋”๋ง์ด ์ด๋ฃจ์–ด์ง€๊ณ  HTML ๋งˆํฌ์—…(markup) ์„ ๋งŒ๋“ค๊ณ  ์ด๋ฅผ DOM ์š”์†Œ ์•ˆ์— ์ฃผ์ž…ํ•˜๋Š” ์ž‘์—…์ด ์ด๋ฃจ์–ด ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

2) ๋ฆฌ๋ Œ๋”๋ง(์กฐํ™” ๊ณผ์ •)

๋ทฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ• ์ ์— "์—…๋ฐ์ดํŠธ ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค" ๋ผ๊ณ  ํ•˜๊ธฐ๋ณด๋‹ค "์กฐํ™” ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค" ๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ์ด ๋” ์ •ํ™•ํ•œ ํ‘œํ˜„์ž…๋‹ˆ๋‹ค. ์ด์œ ๋Š” ์ƒˆ๋กœ์šด DOM ์š”์†Œ๋กœ ๊ฐˆ์•„ ๋ผ์šฐ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋ฆฌ์•กํŠธ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด render ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ๊ณง๋ฐ”๋กœ DOM ์— ๋ฐ˜์˜ํ•˜์ง€ ์•Š๊ณ , ์ด์ „์— render ํ•จ์ˆ˜๊ฐ€ ๋งŒ๋“ค์—ˆ๋˜ ์ปดํฌ๋„ŒํŠธ ์ •๋ณด์™€ ํ˜„์žฌ render ํ•จ์ˆ˜๊ฐ€ ๋งŒ๋“  ์ปดํฌ๋„ŒํŠธ ์ •๋ณด๋ฅผ ๋น„๊ตํ•œํ›„ DOM ํŠธ๋ฆฌ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๋ฆฌ์•กํŠธ์˜ ํŠน์ง•

๋ฆฌ์•กํŠธ์˜ ์ฃผ์š” ํŠน์ง• ์ค‘ ํ•˜๋‚˜๋Š” Virtual DOM์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ

 

์šฐ์„  DOM ์ด ๋ฌด์—‡์ธ์ง€ ๋ถ€ํ„ฐ ์•Œ์•„๋ณด๋ฉด.. DOM ์ด๋ž€ ๋ฌธ์„œ๊ฐ์ฒด ๋ชจ๋ธ(Document Object Model)์˜ ์•ฝ์–ด์ž…๋‹ˆ๋‹ค. ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” DOM ์„ ํ™œ์šฉํ•˜์—ฌ ๊ฐ์ฒด์— ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ CSS ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. DOM ์€ ํŠธ๋ฆฌํ˜•ํƒœ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š”๋ฐ DOM ์„ ํ†ตํ•ด์„œ ํŠน์ • ๋…ธ๋“œ๋ฅผ ์ฐพ๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ์›ํ•˜๋Š” ๊ณณ์— ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ด๋ฏธ์ง€์ถœ์ฒ˜: ์œ„ํ‚ค๋ฐฑ๊ณผ

DOM ์€ ๊ณผ์—ฐ ๋Š๋ฆด๊นŒ?

DOM ์—๋Š” ์น˜๋ช…์ ์ธ ํ•œ ๊ฐ€์ง€ ๋ฌธ์ œ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ ๋™์  UI์— ์ตœ์ ํ™”๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

"์š”์ฆˆ์Œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์€ ๋งค์šฐ ๋น ๋ฅธ ๋ฐ˜๋ฉด, DOM ์€ ๋Š๋ฆฌ๋‹ค?" ๋ผ๊ณ  ํ•˜๋Š”๋ฐ ์ด๋Š” ์ •ํ™•ํ•œ ๋ง์ด ์•„๋‹™๋‹ˆ๋‹ค.

DOM ์ž์ฒด๋Š” ๋น ๋ฆ…๋‹ˆ๋‹ค. ๋‹จ ์›น ๋ธŒ๋ผ์šฐ์ € ๋‹จ์—์„œ DOM ์— ๋ณ€ํ™”๊ฐ€ ์ผ์–ด๋‚˜๋ฉด ์›น๋ธŒ๋ผ์šฐ์ €๊ฐ€ CSS๋ฅผ ๋‹ค์‹œ ์—ฐ์‚ฐํ•˜๊ณ , ๋ ˆ์ด์•„์›ƒ์„ ๊ตฌ์„ฑํ•˜๊ณ , ํŽ˜์ด์ง€๋ฅผ ๋ฆฌํŽ˜์ธํŠธํ•˜๊ฒŒ๋˜๋Š”๋ฐ ์ด ๊ณผ์ •์—์„œ ์‹œ๊ฐ„์ด ํ—ˆ๋น„๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ํ•ด๊ฒฐ๋ฒ•์œผ๋กœ DOM์„ ์ตœ์†Œํ•œ์œผ๋กœ ์กฐ์ž‘ํ•˜์—ฌ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Virtual DOM

์ด๋ฏธ์ง€์ถœ์ฒ˜:&nbsp;speakerdeck

๋ฆฌ์•กํŠธ๋Š” Virtual DOM ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ DOM ์—…๋ฐ์ดํŠธ๋ฅผ ์ถ”์ƒํ™”ํ•จ์œผ๋กœ์จ DOM ์ฒ˜๋ฆฌ ํšŸ์ˆ˜๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ณ  ํšจ์œจ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Virtual DOM ์„ ์‚ฌ์šฉํ•˜๋ฉด ์‹ค์ œ DOM์— ์ ‘๊ทผํ•˜์—ฌ ์กฐ์ž‘ํ•˜๋Š” ๋Œ€์‹ , ์ด๋ฅผ ์ถ”์ƒํ™”ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ ๊ตฌ์„ฑํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋งˆ์น˜ ์‹ค์ œ DOM์˜ ๊ฐ€๋ฒผ์šด ์‚ฌ๋ณธ๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค..

 

๋ฆฌ์•กํŠธ์—์„œ ์‹ค์ œ DOM์„ ์—…๋ฐ์ดํŠธ ํ•  ๋•Œ ์„ธ๊ฐ€์ง€ ์ ˆ์ฐจ๋ฅผ ๋ฐŸ๊ฒŒ ๋˜๋Š”๋ฐ ์‚ดํŽด๋ณด๋ฉด..

1. ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ์ „์ฒด UI๋ฅผ Virtual DOM์— ๋ฆฌ๋ Œ๋”๋ง ํ•œ๋‹ค.

2. ์ด์ „ Virtual DOM์— ์žˆ๋˜ ๋‚ด์šฉ๊ณผ ํ˜„์žฌ ๋‚ด์šฉ์„ ๋น„๊ตํ•œ๋‹ค.

3. ๋ฐ”๋€ ๋ถ€๋ถ„๋งŒ ์‹ค์ œ DOM์— ์ ์šฉํ•œ๋‹ค.

 

์ด์ฒ˜๋Ÿผ  ๋ฆฌ์•กํŠธ๋Š” Virtual DOM ์„ ํ†ตํ•ด์„œ ๋™์  UI ์ตœ์ ํ™” ๋ฌธ์ œ๋ฅผ๊ฐ€์ง€๊ณ  ์žˆ๋Š” DOM ์—…๋ฐ์ดํŠธ ๋ฐฉ์‹์„ ๋ณด์•ˆํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํšจ์œจ์ ์œผ๋กœ ์ž‘์—…์„ ์ง„ํ–‰ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋˜ํ•œ ์ผ๋ถ€ ์›น ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ MVC, MVW ๋“ฑ์˜ ๊ตฌ์กฐ๋ฅผ ์ง€ํ–ฅํ•˜๋Š” ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ ๋ฆฌ์• ํŠธ๋Š” ์˜ค์ง ๋ทฐ๋งŒ ๋‹ด๋‹นํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋กœ์„œ ์ทจํ–ฅ๋Œ€๋กœ ์Šคํƒ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ ์—ฌ๋Ÿฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ ‘ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋‹จ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.


์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” DOCTYPE html ์„ ์–ธ ์˜๋ฏธ๊ฐ€ ๋ฌด์—‡์ธ์ง€์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

<!DOCTYPE html>

HTML ํŒŒ์ผ๋‚ด DOCTYPE ์„ ์„ ์–ธํ•˜๊ฒŒ ๋˜๋ฉด ์ด ๋ฌธ์„œ๋Š” ์›น ํ‘œ์ค€ ๋ฌธ์„œ์ด์ž ๋ชจ๋“  ์›น๋ธŒ๋ผ์šฐ์ €์—์„œ ์ž˜ ๋Œ์•„๊ฐ€๋Š” ํ˜ธํ™˜์ด ๋˜๋Š” ๋ฌธ์„œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
๋ฐ˜๋Œ€๋กœ DOCTYPE ์„ ์„ ์–ธํ•˜์ง€ ์•Š์œผ๋ฉด ๋น„ํ‘œ์ค€ ๋ฌธ์„œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
ํ‘œ์ค€๊ณผ ๋น„ํ‘œ์ค€์˜ ์ฐจ์ด๋Š” ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์„ ์Šค์œ„์น˜ ํ•œ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  html ๋ฌธ์„œ์˜ ์‹œ์ž‘์€ ํ•ด๋‹น ๋ฌธ์„œ๊ฐ€ ์–ด๋–ค ๋ฌธ์„œ ํ˜•์‹์„ ๋”ฐ๋ฅด๊ณ  ์žˆ๋Š”์ง€ ์„ ์–ธํ•˜๋Š” ๊ฒƒ ๋ถ€ํ„ฐ ์‹œ์ž‘๋˜๋ฉฐ, ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ณ„์—†์ด ๋‹จ์ง€ ์„ ์–ธ์„ ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.
HTML ํƒœ๊ทธ ์—ญ์‹œ ๋Œ€๋ฌธ์ž์™€ ์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ณ„ํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ๊ฐ€๊ธ‰์  ์†Œ๋ฌธ์ž ์‚ฌ์šฉ์„ ๊ถŒ๊ณ ํ•ฉ๋‹ˆ๋‹ค.

DOCTYPE ์„ ์–ธ์„ ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์™€ ์„ ์–ธํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ํŒŒ์ด์–ดํญ์Šค ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ™•์ธํ•ด ๋ณด๋ฉด..

DOCTYPE ๋น„์„ ์–ธ


DOCTYPE ์„ ์–ธ


๋ธŒ๋ผ์šฐ์ €์˜ ๋ Œ๋”๋ง ๋ฐฉ์‹์ด DOCTYPE ์„ ์„ ์–ธํ•˜์ง€ ์•Š์œผ๋ฉด ์ฟผํฌ๋ชจ๋“œ(Quirks mode) ์ด๊ณ  ์„ ์–ธํ•˜๋ฉด ํ‘œ์ค€ ํ˜ธํ™˜ ๋ชจ๋“œ(Standard mode) ๋กœ ์ฐจ์ด๊ฐ€ ์žˆ๋Š”๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋”ฐ๋ผ ์ปจํ…์ธ ๋ฅผ ๋ Œ๋”๋ง ํ•˜๋Š” ์œ„์น˜๋„ ์กฐ๊ธˆ ๋‹ค๋ฅธ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ €๋Š” ์ถœ๋ ฅํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ๊ฐ€ ์ตœ์‹ ์ด๋ผ๊ณ  ํŒ๋‹จํ•˜๋ฉด ํ‘œ์ค€๋ชจ๋“œ๋กœ ๋ Œ๋”๋ง์„ ํ•˜๊ณ , ๋ฐ˜๋ฉด ์˜ˆ์ „ ๋ฌธ์„œ๋ผ๊ณ  ํŒ๋‹จํ•˜๋ฉด ์ฟผํฌ๋ชจ๋“œ๋กœ ๋ Œ๋”๋ง์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ฟผํฌ ๋ชจ๋“œ์˜ ๋ชฉ์ ์€ ์˜ค๋ž˜๋œ ์›นํŽ˜์ด์ง€๋“ค์ด ์ตœ์‹  ๋ฒ„์ „์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋‹จ์ง€ ๊นจ์ ธ ๋ณด์ด์ง€ ์•Š์œผ๋ ค๋Š” ๊ฒƒ์— ์žˆ๊ณ  ๋น„ํ‘œ์ค€์  ๋ฐฉ๋ฒ•์˜ CSS๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ค‘์š”ํ•œ๊ฑด.. ์›น ํ‘œ์ค€์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์„œ๊ฐ€ ์˜ค๋Š˜๋‚  ์กด์žฌํ•˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์—.. HTML์€ DOCTYPE ์„ ์„ ์–ธํ•˜๊ณ  ํ‘œ์ค€์œผ๋กœ ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

+ Recent posts