๋ฐœํ–‰์ผ:

์ˆ˜์ •์ผ:

์—‘์…€ ์ž๋™ํ™” ์ธ๋ณด์ด์Šค/๋ณด๊ณ ์„œ ์ƒ์„ฑ Flask ๊ธฐ๋ฐ˜ ์›น ์•ฑ ๋งŒ๋“ค๊ธฐ

์—‘์…€ ์ž๋™ํ™” Flask ๊ธฐ๋ฐ˜ ์›น ์•ฑ์œผ๋กœ ์—‘์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ์ธ๋ณด์ด์Šค์™€ ์ž๋™ ๋ณด๊ณ ์„œ๋กœ ๋ณ€ํ™˜ํ•˜์„ธ์š”. ์—…๋กœ๋“œ, ํŽธ์ง‘, ๋ฏธ๋ฆฌ๋ณด๊ธฐ, PDF ์ถœ๋ ฅ๊นŒ์ง€ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.


์—‘์…€ ์ž๋™ํ™” ์›น ์•ฑ์˜ ์†Œ๊ฐœ ๋ฐ ํ•„์š”์„ฑ

๋””์ง€ํ„ธ ์›Œํฌํ”Œ๋กœ์šฐ์—์„œ ์—‘์…€(Excel)์€ ์—ฌ์ „ํžˆ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์˜ ํ•ต์‹ฌ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ •ํ˜•ํ™”๋œ ๋ณด๊ณ ์„œ, ์ธ๋ณด์ด์Šค, ๊ณ„์•ฝ์„œ ๋“ฑ ์‹œ๊ฐ์  ๋ฌธ์„œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์€ ๋ฐ˜๋ณต์ ์ด๊ณ  ์‹œ๊ฐ„์ด ๋งŽ์ด ์†Œ์š”๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ˆ˜๋™ ๋ณต์‚ฌ/๋ถ™์—ฌ๋„ฃ๊ธฐ ๊ณผ์ •์€ ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์œ„ํ—˜ ๋˜ํ•œ ๋†’์Šต๋‹ˆ๋‹ค. ์ด๋•Œ ํ•„์š”ํ•œ ๊ฒƒ์€ ์ž๋™ํ™” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋ฐ”๋กœ ์ด ์ง€๋ฃจํ•˜๊ณ  ๋น„ํšจ์œจ์ ์ธ ๊ฐ„๊ทน์„ ๋ฉ”์šฐ๋Š” ํ•ด๋ฒ•์ด ๋ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŒŒ์ด์ฌ์„ ํ™œ์šฉํ•ด์„œ Flask, Pandas,๋ผ๋Š” ์กฐํ•ฉ์„ ํ™œ์šฉํ•˜์—ฌ, ์—‘์…€ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ํ‘œ์ค€ํ™”๋œ ์›น ๊ธฐ๋ฐ˜ ๋ฌธ์„œ๋ฅผ ์ฆ‰์‹œ ์ƒ์„ฑ, ํŽธ์ง‘, ์ถœ๋ ฅ(PDF)๊นŒ์ง€ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒฝ๋Ÿ‰ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์ž๋™ํ™” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

๋ณธ ๊ธ€์—์„œ๋Š” ์ด ์ฝ”๋“œ์˜ ๊ธฐ์ˆ ์  ๊ตฌ์กฐ, ๊ตฌํ˜„ ์›๋ฆฌ, ๋น„์ฆˆ๋‹ˆ์Šค์  ๊ฐ€์น˜๋ฅผ ์•Œ์•„๋ณด๊ณ  ๋‹ค๋ฃจ์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


์ž๋™ํ™” ์—‘์…€ ์ž๋™ ์–‘์‹ ๋ณ€ํ™˜์˜ ์›๋ฆฌ

์ด ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์—‘์…€ ์–‘์‹์„ ์ž๋™์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ํ•ต์‹ฌ ๋ฐฉ๋ฒ•์€ Python์„ ์ค‘์‹ฌ์œผ๋กœ ํ•œ ์„ธ ๊ฐ€์ง€ ์ฃผ์š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

1. ์›น ํ”„๋ ˆ์ž„์›Œํฌ: Flask (๋งˆ์ดํฌ๋กœ ํ”„๋ ˆ์ž„์›Œํฌ ํ™œ์šฉ)

Flask๋Š” Django์™€ ๋‹ฌ๋ฆฌ ํ•„์ˆ˜์ ์ธ ๊ธฐ๋Šฅ๋งŒ ์ œ๊ณตํ•˜๋Š” ๋งˆ์ดํฌ๋กœ ์›น ํ”„๋ ˆ์ž„์›Œํฌ(Micro Web Framework)์ž…๋‹ˆ๋‹ค. Flask๋ฅผ ์„ ํƒํ•œ ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๊ฒฝ๋Ÿ‰์„ฑ ๋ฐ ์†๋„: ์ธ๋ณด์ด์Šค ์ƒ์„ฑ ์•ฑ์ฒ˜๋Ÿผ ํŠน์ • ๋ชฉ์ ์„ ์œ„ํ•œ ์†Œ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์— ๊ฐ€์žฅ ์ ํ•ฉํ•˜๋ฉฐ, ๋ถˆํ•„์š”ํ•œ ๊ธฐ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์—†์–ด ์‹คํ–‰ ์†๋„๊ฐ€ ๋น ๋ฆ…๋‹ˆ๋‹ค.
  • ์œ ์—ฐ์„ฑ: ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Pandas, Openpyxl ๋“ฑ)๋ฅผ ์ž์œ ๋กญ๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ชจ๋“ˆ์„ฑ: ๋ผ์šฐํŠธ(@app.route), ํ…œํ”Œ๋ฆฟ(render_template), ์„ธ์…˜ ๊ด€๋ฆฌ ๋“ฑ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ด ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์–ด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.

2. ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์—”์ง„: Pandas (์—‘์…€ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•)

Pandas๋Š” ํŒŒ์ด์ฌ์—์„œ ๋ฐ์ดํ„ฐ ์กฐ์ž‘ ๋ฐ ๋ถ„์„์„ ์œ„ํ•œ ์‚ฌ์‹ค์ƒ์˜ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

  • DataFrame์˜ ์œ„๋ ฅ: ์—‘์…€ ํŒŒ์ผ์„ pd.read_excel(BytesIO(file_data))๋ฅผ ํ†ตํ•ด DataFrame์ด๋ผ๋Š” 2์ฐจ์› ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๋กœ ์ฆ‰์‹œ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ฐ์ดํ„ฐ์˜ ํ–‰๊ณผ ์—ด ์ ‘๊ทผ์„ ๋งค์šฐ ์ง๊ด€์ ์ด๊ณ  ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ: ์ฝ”๋“œ์—์„œ df.fillna(''), df.astype(str), df.dropna(how='all') ๋“ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—‘์…€์˜ ๋นˆ ๊ฐ’(NaN), ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๋ฅผ ์‚ฌ์ „์— ์ฒ˜๋ฆฌํ•จ์œผ๋กœ์จ, "Garbage In, Garbage Out"์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ถœ๋ ฅ ๋ฌธ์„œ์˜ ํ’ˆ์งˆ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

3. ํŒŒ์ผ ์ž…์ถœ๋ ฅ ๋ฐ ๋ฐฐํฌ: Openpyxl ์™€ PyInstaller

  • openpyxl: Pandas๊ฐ€ .xlsx ํฌ๋งท์˜ ์—‘์…€ ํŒŒ์ผ์„ ์ฝ๊ณ  ์“ฐ๋Š” ๋ฐ ํ•„์š”ํ•œ ์—”์ง„์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ๋Š” ํŒŒ์ผ์„ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ˜•ํƒœ๋กœ ์ฝ์–ด ๋“ค์ด๋ฏ€๋กœ, ์ด ์˜์กด์„ฑ์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.
  • PyInstaller: ํŒŒ์ด์ฌ ํ™˜๊ฒฝ์ด ์—†๋Š” ๊ณณ์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์•ฑ ์ „์ฒด๋ฅผ ๋‹จ์ผ ์‹คํ–‰ ํŒŒ์ผ(.exe)๋กœ ๋ฌถ์–ด์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ํ™˜๊ฒฝ์—์„œ ๋ฐฐํฌ ๋ฐ ์‚ฌ์šฉ ํŽธ์˜์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค

์ปดํŒŒ์ผ ๋ช…๋ น์–ด

pyinstaller--onefile--nameInvoiceApp--add-data"templates;templates"--add-data"static;static"--collect-allopenpyxlapp.py

์—‘์…€ ์ž๋™ํ™” ๋ฌธ์„œ ์ž‘์„ฑ ํ”„๋กœ๊ทธ๋žจ ์ธ๋ณด์ด์Šค ์ž๋™ํ™” ๋ฌธ์„œ ์ž‘์„ฑ ํผ ํ™”๋ฉด

์—‘์…€ ๋ฐ์ดํ„ฐ์˜ ๋…ผ๋ฆฌ์  ํ๋ฆ„ ๋ฐ ๋ฌธ์„œ ๋ณ€ํ™˜ ๋ฐฉ๋ฒ•

์•„๋ž˜ ์ฝ”๋“œ๋Š” ์—‘์…€ ๋ฐ์ดํ„ฐ๋ฅผ "๋‹จ์ˆœํžˆ ์ฝ๋Š” ๊ฒƒ"์„ ๋„˜์–ด, ํ…œํ”Œ๋ฆฟ์„ ๊ตฌ๋™์‹œํ‚ค๋Š” ๋™์  ๋ณ€์ˆ˜๋กœ ํ™œ์šฉํ•˜๋Š” ๋ฐ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ๋ฐ์ดํ„ฐ ์ถ”์ถœ๊ณผ ์ฝ”๋“œํ™” (Code Mapping)

์—‘์…€ ํŒŒ์ผ์ด ์—…๋กœ๋“œ๋˜๋ฉด, ์„œ๋ฒ„๋Š” ํŒŒ์ผ์„ ์ฝ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋…ผ๋ฆฌ์  ์ถ”๋ก ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์—‘์…€ ์—ด (์ถ”์ •) ์šฉ๋„ Python ๋ณ€์ˆ˜ ์ €์žฅ ์œ„์น˜
A์—ด (์ธ๋ฑ์Šค 0) ์ฝ”๋“œ(Code): INVOICE_NO, SELLER, TOTAL_AMT ๋“ฑ ํ‚ค ๊ฐ’ code_values ๋”•์…”๋„ˆ๋ฆฌ์˜ ํ‚ค
B์—ด (์ธ๋ฑ์Šค 1) ์„ค๋ช…(Description): ์ฝ”๋“œ์— ๋Œ€ํ•œ ๋ถ€๊ฐ€ ์„ค๋ช… code_values[code]['description']
C์—ด (์ธ๋ฑ์Šค 2) ๊ฐ’(Value): ํ…œํ”Œ๋ฆฟ์— ์‚ฝ์ž…๋  ์‹ค์ œ ๋ฐ์ดํ„ฐ code_values[code]['value']
  • ์ฝ”๋“œ๋Š” XL_ ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋Ÿฌํ•œ ์—‘์…€ ์ฝ”๋“œ๋ฅผ ํ…œํ”Œ๋ฆฟ ๋ณ€์ˆ˜๋กœ ํ‰ํƒ„ํ™”(template_data['XL_' + code] = data.get('value'))ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ ์—‘์…€์˜ ํŠน์ • ์…€์— ํ• ๋‹น๋œ ๊ฐ’์„ ๋ฌธ์„œ ๋‚ด์˜ ํŠน์ • ์œ„์น˜์— ์ •ํ™•ํ•˜๊ฒŒ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

์˜ˆ:

์—‘์…€ ํŒŒ์ผ ๋‚ด์šฉ

20 Documentary Credit Number 508434578

์–‘์‹ ํ…œํ”Œ๋ฆฟ ์ ์šฉ

h2 Invoice:{{XL_20}} /h2

์ถœ๋ ฅ

Invoice:508434578

2. ํ…œํ”Œ๋ฆฟ ์—”์ง„ (Jinja2)์˜ ์—ญํ• 

render_template(TEMPLATE_MAP[template_key], get_template_data()) ํ˜ธ์ถœ์€ Jinja2 ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.

  • ๋™์  ๋‚ด์šฉ ์‚ฝ์ž…: Jinja2๋Š” HTML ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์—์„œ {{ ๋ณ€์ˆ˜๋ช… }} ํ˜•ํƒœ์˜ ํ”Œ๋ ˆ์ด์Šคํ™€๋”๋ฅผ ์ฐพ์•„ get_template_data()์—์„œ ์ œ๊ณต๋œ ์‹ค์ œ ๋ฐ์ดํ„ฐ ๊ฐ’์œผ๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, {{ XL_10 }}์€ ์—‘์…€์˜ ์ฝ”๋“œ 10๋ฒˆ ์…€์— ์žˆ๋˜ ๊ฐ’์œผ๋กœ ์ฑ„์›Œ์ง‘๋‹ˆ๋‹ค.
  • ๋ฐ˜๋ณต๋ฌธ ์ฒ˜๋ฆฌ (์•„์ดํ…œ ๋ชฉ๋ก): ์—‘์…€์˜ ํ–‰ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์€ ๊ฒฝ์šฐ(์˜ˆ: ํ’ˆ๋ชฉ ๋ชฉ๋ก), Jinja2์˜ {% for item in items %} ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ํ…Œ์ด๋ธ” ํ–‰์œผ๋กœ ์ž๋™ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ์จ ๋ฐ์ดํ„ฐ์˜ ์–‘๊ณผ ๊ด€๊ณ„์—†์ด ๋ฌธ์„œ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ์ด ๊ธˆ์•ก ๊ณ„์‚ฐ ๋ฐ ํ˜•์‹ํ™”

get_template_data ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ df['Amount'].astype(float).sum():,.2f} EUR๋ฅผ ํ†ตํ•ด ์ด ๊ธˆ์•ก์„ ๊ณ„์‚ฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ฐ•๋ ฅํ•œ ํ˜•์‹ํ™”: ๋‹จ์ˆœํžˆ ํ•ฉ๊ณ„๋งŒ ๊ตฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, :.2f๋ฅผ ํ†ตํ•ด ์†Œ์ˆ˜์  ๋‘˜์งธ ์ž๋ฆฌ๊นŒ์ง€ ํ‘œ์‹œํ•˜๊ณ , ,๋ฅผ ํ†ตํ•ด ์ฒœ ๋‹จ์œ„ ๊ตฌ๋ถ„ ๊ธฐํ˜ธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉฐ, ํ†ตํ™” ๊ธฐํ˜ธ(EUR)๊นŒ์ง€ ๋ถ™์—ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋ฌธ์„œ ํ‘œ์ค€์— ๋งž๋Š” ์ตœ์ข… ์ถœ๋ ฅ๊ฐ’์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

๋ฌธ์„œ ๊ด€๋ฆฌ ๋ฐ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ (UX) ์ตœ์ ํ™”

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

1. ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ƒํƒœ ๊ด€๋ฆฌ (์„ธ์…˜๊ณผ ํžˆ์Šคํ† ๋ฆฌ)

์ฝ”๋“œ๋Š” ์„ธ์…˜(Session)์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ƒํƒœ๋ฅผ ์˜์†์ ์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

  • session['excel_file']: ์—…๋กœ๋“œ๋œ ์—‘์…€ ๋ฐ์ดํ„ฐ ํŒŒ์ผ๋ช…(๋””์Šคํฌ ์ €์žฅ)์„ ๊ธฐ์–ตํ•ฉ๋‹ˆ๋‹ค.
  • session['current_preview_file']: ํ˜„์žฌ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ๋ฏธ๋ฆฌ๋ณด๊ธฐ HTML ํŒŒ์ผ๋ช…์„ ๊ธฐ์–ตํ•ฉ๋‹ˆ๋‹ค.
  • session['preview_history']: ์‚ฌ์šฉ์ž๊ฐ€ ์—ฌ๋Ÿฌ ํ…œํ”Œ๋ฆฟ์„ ๋กœ๋“œํ•˜๊ฑฐ๋‚˜ ์ด์ „/๋‹ค์Œ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ์˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ธฐ๋ก์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ด ํžˆ์Šคํ† ๋ฆฌ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž๋Š” ์—ฌ๋Ÿฌ ํ…œํ”Œ๋ฆฟ ๊ฐ„์˜ ์ „ํ™˜์„ ์ž์œ ๋กญ๊ฒŒ ๋˜๋Œ๋ฆฌ๊ฑฐ๋‚˜ ์•ž์œผ๋กœ ๋‚˜์•„๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ํŽธ์ง‘ ๋‚ด์šฉ ์œ ์ง€ ๋ฐ ์ €์žฅ (save_edited)

๊ฐ€์žฅ ์ค‘์š”ํ•œ UX ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” ํŽธ์ง‘ ๊ฐ€๋Šฅ ๋ฏธ๋ฆฌ๋ณด๊ธฐ(ContentEditable)์™€ ์ˆ˜์ • ๋‚ด์šฉ ์œ ์ง€ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

  • contenteditable="true": iframe ๋‚ด๋ถ€์— HTML์„ ๋กœ๋“œํ•˜๋Š” ๋Œ€์‹ , ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ Div ํƒœ๊ทธ๋กœ ๊ฐ์‹ธ๊ณ  ์ด ์†์„ฑ์„ ๋ถ€์—ฌํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ง์ ‘ ํ…์ŠคํŠธ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • session['edited_file']: ์‚ฌ์šฉ์ž๊ฐ€ '์ €์žฅ' ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ˆ˜์ •ํ•œ HTML์ด ๋””์Šคํฌ์— ๋ณ„๋„ ํŒŒ์ผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ดํ›„ ๊ฐ™์€ ํ…œํ”Œ๋ฆฟ์„ ๋‹ค์‹œ ๋กœ๋“œํ•  ๋•Œ, ์ฝ”๋“œ๋Š” ๋ Œ๋”๋ง์„ ๋‹ค์‹œ ํ•˜์ง€ ์•Š๊ณ  ์ด ํŽธ์ง‘๋œ ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์™€ ์ˆ˜์ • ๋‚ด์šฉ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

3. PDF ์ถœ๋ ฅ ๋ฐฉ๋ฒ•

์‚ฌ์šฉ์ž ์š”์ฒญ์— ๋”ฐ๋ผ printPreview() ํ•จ์ˆ˜๋Š” PDF ์ €์žฅ ๊ธฐ๋Šฅ์„ ๋Œ€์‹ ํ•˜๋„๋ก ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

  • ๋ธŒ๋ผ์šฐ์ € ์ธ์‡„ API ํ™œ์šฉ: window.open()์„ ํ†ตํ•ด ํ…œํ”Œ๋ฆฟ HTML์„ ์ƒˆ ์ฐฝ์— ๋กœ๋“œํ•˜๊ณ , printWindow.print()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์˜ ๊ธฐ๋ณธ ์ธ์‡„ ๋Œ€ํ™” ์ƒ์ž๋ฅผ ๋„์›๋‹ˆ๋‹ค.
  • PDF ์ถœ๋ ฅ: ์‚ฌ์šฉ์ž๋Š” ์ธ์‡„ ๋Œ€์ƒ(Destination)์„ "PDF๋กœ ์ €์žฅ(Save as PDF)"์œผ๋กœ ์„ ํƒํ•จ์œผ๋กœ์จ, ๋ธŒ๋ผ์šฐ์ € ์—”์ง„์ด ์ œ๊ณตํ•˜๋Š” CSS ๋ฐ ์ธ์‡„ ์Šคํƒ€์ผ์ด ์™„๋ฒฝํ•˜๊ฒŒ ์ ์šฉ๋œ PDF ๋ฌธ์„œ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์„œ๋ฒ„์—์„œ PDF ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์ด๋ฉฐ, ๋ Œ๋”๋ง ์ •ํ™•๋„๊ฐ€ ๋†’์Šต๋‹ˆ๋‹ค.

๋น„์ฆˆ๋‹ˆ์Šค ๊ฐ€์น˜ ๋ฐ ํ™•์žฅ์„ฑ

์ด์™€ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ๋‹จ์ˆœํ•œ ๊ธฐ์ˆ  ํ”„๋กœ์ ํŠธ๋ฅผ ๋„˜์–ด, ์ค‘์†Œ๊ธฐ์—…(SME) ๋ฐ ๊ฐœ์ธ ์‚ฌ์—…์ž(Freelancer)์—๊ฒŒ ์‹ค์งˆ์ ์ธ ๋น„์ฆˆ๋‹ˆ์Šค ํšจ์œจ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

1. ์ƒ์‚ฐ์„ฑ ๋ฐ ๋น„์šฉ ์ ˆ๊ฐ

  • ์‹œ๊ฐ„๋‹น ์ƒ์‚ฐ์„ฑ ์ฆ๊ฐ€ (TPO): ๋งค๋ฒˆ ์ˆ˜๋™์œผ๋กœ ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•˜๋˜ ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•˜์—ฌ, ์ง์›์ด ํ•ต์‹ฌ ์—…๋ฌด(๋ถ„์„, ์˜์—… ๋“ฑ)์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ํ…œํ”Œ๋ฆฟ ์žฌ์‚ฌ์šฉ: ๋‹ค์–‘ํ•œ ํ…œํ”Œ๋ฆฟ ์˜ต์…˜(Standard, Report, Memo ๋“ฑ 10๊ฐœ)์„ ์ œ๊ณตํ•˜์—ฌ, ๋™์ผํ•œ ์—‘์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ๋ชฉ์ ์˜ ๋ฌธ์„œ๋กœ ์ฆ‰์‹œ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ๋ฐ์ดํ„ฐ ๊ฑฐ๋ฒ„๋„Œ์Šค ๋ฐ ์˜ค๋ฅ˜ ๊ฐ์†Œ

  • ๋ฐ์ดํ„ฐ ์ถœ์ฒ˜ ๋‹จ์ผํ™”: ๋ชจ๋“  ๋ฌธ์„œ๊ฐ€ ํ•˜๋‚˜์˜ ์—‘์…€ ํŒŒ์ผ์—์„œ ์‹œ์ž‘๋˜๋ฏ€๋กœ, ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ(Data Consistency)์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.
  • ์ž๋™ ๊ฒ€์ฆ: Pandas๊ฐ€ ๋ฐ์ดํ„ฐ ํ˜•์‹ ์˜ค๋ฅ˜๋ฅผ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , ์ดํ•ฉ ๊ณ„์‚ฐ์„ ์ •ํ™•ํžˆ ์ˆ˜ํ–‰ํ•จ์œผ๋กœ์จ ์ˆ˜๋™ ๊ณ„์‚ฐ๊ณผ ์ž‘์—… ์˜ค๋ฅ˜๋ฅผ 0%๋กœ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ (Microservices ๊ตฌ์กฐ)

Flask ์•ฑ์€ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค(Microservices) ์•„ํ‚คํ…์ฒ˜๋กœ ํ™•์žฅํ•˜๊ธฐ ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.

  • API ๋ถ„๋ฆฌ: ํ˜„์žฌ์˜ /upload, /load_template ๋ผ์šฐํŠธ๋ฅผ RESTful API๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๋‹ค๋ฅธ ์‹œ์Šคํ…œ(์˜ˆ: CRM, ERP)๊ณผ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ: ๋Œ€์šฉ๋Ÿ‰ ์—‘์…€ ํŒŒ์ผ ์ฒ˜๋ฆฌ ์‹œ, upload ๊ธฐ๋Šฅ์„ Celery์™€ ๊ฐ™์€ ๋น„๋™๊ธฐ ์ž‘์—… ํ์— ๋„˜๊ฒจ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”„๋กœ๊ทธ๋žจ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•

ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ(Project Directory Structure)

project/
โ”œโ”€โ”€app.py
โ”œโ”€โ”€static/
โ”‚โ””โ”€โ”€img/
โ”‚โ””โ”€โ”€sin.jpg
โ””โ”€โ”€templates/
โ””โ”€โ”€invoice.html

ํŒŒ์ด์ฌ ํŒŒ์ผ index. html ํŒŒ์ผ

app.py
0.01MB

index.html
0.00MB

HTML ๋ฐ์ดํ„ฐ ๋งคํ•‘ ํ…Œ์ด๋ธ”

Code Variable Description Template Call
40AXL_40AForm of Documentary Credit{{ XL_40A }}
20XL_20Documentary Credit Number{{ XL_20 }}
31CXL_31CDate of Issue{{ XL_31C }}
40EXL_40EApplicable Rules{{ XL_40E }}
31DXL_31DDate and Place of Expiry{{ XL_31D }}
50XL_50Applicant{{ XL_50 }}
59XL_59Beneficiary{{ XL_59 }}
32BXL_32BCurrency, Amount{{ XL_32B }}
39AXL_39ATolerance{{ XL_39A }}
41aXL_41aAvailable With{{ XL_41a }}
42PXL_42PNegotiation Details{{ XL_42P }}
43PXL_43PPartial Shipments{{ XL_43P }}
43TXL_43TTranshipment{{ XL_43T }}
44EXL_44EPort of Loading{{ XL_44E }}
44FXL_44FPort of Discharge{{ XL_44F }}
44CXL_44CLatest Shipment Date{{ XL_44C }}
45AXL_45AGoods Description{{ XL_45A }}
46AXL_46ADocuments Required{{ XL_46A }}
47AXL_47AAdditional Conditions{{ XL_47A }}
49GXL_49GSpecial Payment Conditions{{ XL_49G }}
71DXL_71DCharges{{ XL_71D }}
48XL_48Period for Presentation{{ XL_48 }}
49XL_49Confirmation Instructions{{ XL_49 }}
78XL_78Instructions to Bank{{ XL_78 }}
72ZXL_72ZSender to Receiver Info{{ XL_72Z }}

ํŒŒ์ด์ฌ ๊ธฐ๋ฐ˜ ์ž๋™ํ™”์˜ ๊ฐ€์น˜

์ตœ๊ทผ Python์˜ ์ƒํƒœ๊ณ„๋Š” ๋‹จ์ˆœํ•œ ์Šคํฌ๋ฆฝํŒ…์„ ๋„˜์–ด, ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋ฌธ์ œ ํ•ด๊ฒฐ์„ ์œ„ํ•œ ๋…๋ฆฝ์ ์ธ ์†Œํ”„ํŠธ์›จ์–ด ์†”๋ฃจ์…˜์œผ๋กœ ๋ฐœ์ „ํ–ˆ์Œ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

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


์—‘์…€ ์ž๋™ํ™” ์›น ์•ฑ์€ ์–ด๋–ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„๋˜๋‚˜์š”?

์ด ์›น ์•ฑ์€ Python ๊ธฐ๋ฐ˜์œผ๋กœ Flask, Pandas, Openpyxl, Jinja2 ๋“ฑ์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. Flask๋Š” ๊ฒฝ๋Ÿ‰ ๋งˆ์ดํฌ๋กœ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ์›น ์„œ๋ฒ„์™€ ๋ผ์šฐํŒ…์„ ๋‹ด๋‹นํ•˜๊ณ , Pandas๋Š” ์—‘์…€ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ฝ๊ณ  ์ฒ˜๋ฆฌํ•˜๋ฉฐ, Openpyxl์€ ์—‘์…€ ํŒŒ์ผ ์ž…์ถœ๋ ฅ์„ ์ง€์›ํ•˜๊ณ , Jinja2๋Š” HTML ํ…œํ”Œ๋ฆฟ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋™์ ์œผ๋กœ ์‚ฝ์ž…ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

์—‘์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์›น ๋ฌธ์„œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์€ ์–ด๋–ป๊ฒŒ ์ด๋ฃจ์–ด์ง€๋‚˜์š”?

์—…๋กœ๋“œ๋œ ์—‘์…€ ํŒŒ์ผ์€ Pandas DataFrame์œผ๋กœ ๋ณ€ํ™˜๋˜์–ด ๊ฐ ์—ด๊ณผ ํ–‰์ด Python ๋ณ€์ˆ˜์— ๋งคํ•‘๋ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ์™€ ๊ฐ’, ์„ค๋ช… ๋“ฑ์€ ๋”•์…”๋„ˆ๋ฆฌ ๊ตฌ์กฐ๋กœ ๊ด€๋ฆฌ๋˜๋ฉฐ, Jinja2 ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ํ†ตํ•ด HTML ๋ฌธ์„œ ๋‚ด {{ ๋ณ€์ˆ˜ }} ์œ„์น˜์— ์‹ค์ œ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฝ์ž…๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ณต๋˜๋Š” ๋ชฉ๋ก์€ {% for %} ๋ฃจํ”„๋ฅผ ํ†ตํ•ด ์ž๋™์œผ๋กœ ํ…Œ์ด๋ธ” ํ–‰์œผ๋กœ ์ƒ์„ฑ๋˜๋ฉฐ, ์ด ๊ธˆ์•ก ๊ณ„์‚ฐ๊ณผ ํ†ตํ™” ํ˜•์‹ํ™”๊นŒ์ง€ ์ž๋™ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ์ˆ˜์ •ํ•œ ๋‚ด์šฉ์ด๋‚˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ƒํƒœ๋Š” ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌ๋˜๋‚˜์š”?

์•ฑ์€ ์„ธ์…˜(Session)์„ ์‚ฌ์šฉํ•˜์—ฌ ์—…๋กœ๋“œ๋œ ์—‘์…€ ํŒŒ์ผ, ํ˜„์žฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํŒŒ์ผ, ์ด์ „/๋‹ค์Œ ํžˆ์Šคํ† ๋ฆฌ ๋“ฑ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. contenteditable ์†์„ฑ์„ ํ™œ์šฉํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ณ , '์ €์žฅ' ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ˆ˜์ •๋œ HTML์ด ๋ณ„๋„ ํŒŒ์ผ๋กœ ์ €์žฅ๋˜์–ด ์žฌ๋กœ๋“œ ์‹œ ํŽธ์ง‘ ๋‚ด์šฉ์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

PDF ์ถœ๋ ฅ์€ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„๋˜๋‚˜์š”?

์‚ฌ์šฉ์ž๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ '์ธ์‡„' ๊ธฐ๋Šฅ์„ ํ˜ธ์ถœํ•˜๋ฉด, ์ƒˆ ์ฐฝ์— HTML ํ…œํ”Œ๋ฆฟ์„ ๋กœ๋“œํ•˜๊ณ  printWindow.print()๋กœ ๋ธŒ๋ผ์šฐ์ € ์ธ์‡„ ๋Œ€ํ™”์ƒ์ž๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ 'PDF๋กœ ์ €์žฅ'์„ ์„ ํƒํ•˜๋ฉด CSS ์Šคํƒ€์ผ์ด ์ ์šฉ๋œ ๊ณ ํ’ˆ์งˆ PDF ๋ฌธ์„œ๋ฅผ ์ฆ‰์‹œ ์–ป์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์„œ๋ฒ„์—์„œ PDF ๋ณ€ํ™˜์„ ์ฒ˜๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์–ด ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์ž…๋‹ˆ๋‹ค.

์ด ์ž๋™ํ™” ์•ฑ์˜ ๋น„์ฆˆ๋‹ˆ์Šค์  ์žฅ์ ์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

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

https://everydayhub.tistory.com/1150

https://everydayhub.tistory.com/1154

'IT > tech-resources' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

AI ์‹œ๋Œ€ ์ฝ˜ํ…์ธ  ์ง„์ •์„ฑ ์ฆ๋ช…๋ฐฉ๋ฒ•! ์ธ๊ฐ„ ์›Œํ„ฐ๋งˆํฌ ํ”„๋กœ๊ทธ๋žจ ์†Œ๊ฐœ  (1) 2025.12.31
๋ธ”๋กœ๊ทธ ๋ฐฉ๋ฌธ์ž ๋Š˜๋ฆฌ๋Š” ๋น„๋ฐ€ ์ฝ”๋“œ ๊ณต์œ , ๊ณต๊ฐœ ์ „ ๋ฐ˜๋“œ์‹œ ์•Œ์•„์•ผ ํ•  ์‚ฌ์‹ค  (0) 2025.11.26
GAS CORS ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: Simple Request ์ „๋žต์œผ๋กœ API ํ†ต์‹  ์•ˆ์ •ํ™”  (0) 2025.11.25
๋ธ”๋กœ๊ทธ ๋ณต์‚ฌ ์ถ”์  ๋””์ง€ํ„ธ ์ง€๋ฌธ์œผ๋กœ 100% ์ž๋™ํ™” (์ฝ”๋“œ ๊ณต์œ )  (0) 2025.11.15
๋„์šฉ๋ฐฉ์ง€! ๋ธ”๋กœ๊ทธ์—์„œ ๋””์ง€ํ„ธ ์ง€๋ฌธ(Digital Fingerprinting) ์‚ฌ์šฉ ๋ฐฉ๋ฒ• ์†Œ๊ฐœ  (0) 2025.11.14
๋ณต์‚ฌ๋ฐฉ์ง€ ํ…์ŠคํŠธ ์„ ํƒ ์ฐจ๋‹จ์ด UXยท์ ‘๊ทผ์„ฑยทSEO์— ๋ผ์น˜๋Š” ์˜ํ–ฅ๊ณผ ํ•ด๊ฒฐ๋ฒ•  (0) 2025.11.14
๋งคํฌ๋กœ ๋Œ“๊ธ€ ๊ตฌ๋ถ„ํ•˜๋Š” ๋ฐฉ๋ฒ•: ๋งคํฌ๋กœ ๋Œ“๊ธ€๊ณผ ์‹ค์ œ ์‚ฌ๋žŒ ๋Œ“๊ธ€ ์‹œ๊ฐ์  ํ‘œ์‹œํ•˜๊ธฐ  (0) 2025.10.06