Update app.py
Browse files
app.py
CHANGED
@@ -27,14 +27,8 @@ def save_binary_file(file_name, data):
|
|
27 |
logger.debug(f"ํ์ผ ์ ์ฅ ์๋ฃ: {file_name}")
|
28 |
|
29 |
|
30 |
-
def
|
31 |
-
"
|
32 |
-
with open(img_path, "rb") as f:
|
33 |
-
return f.read()
|
34 |
-
|
35 |
-
|
36 |
-
def merge_images(person_img_path, product_img_path, background_img_path, prompt, model="gemini-2.0-flash-exp-image-generation"):
|
37 |
-
logger.debug(f"merge_images ํจ์ ์์ - ํ๋กฌํํธ: '{prompt}'")
|
38 |
|
39 |
try:
|
40 |
# API ํค๋ ํ๊ฒฝ๋ณ์์์ ๋ถ๋ฌ์ด
|
@@ -48,48 +42,18 @@ def merge_images(person_img_path, product_img_path, background_img_path, prompt,
|
|
48 |
client = genai.Client(api_key=effective_api_key)
|
49 |
logger.debug("Gemini ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ์๋ฃ.")
|
50 |
|
51 |
-
#
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
product_file = client.files.upload(file=product_img_path)
|
56 |
-
logger.debug(f"์ํ ์ด๋ฏธ์ง ์
๋ก๋ ์๋ฃ: {product_file.uri}")
|
57 |
|
|
|
|
|
|
|
|
|
58 |
# ์ปจํ
์ธ ๋ฆฌ์คํธ ์์ฑ
|
59 |
-
parts = [
|
60 |
-
|
61 |
-
# ์ฌ๋ ์ด๋ฏธ์ง ์ถ๊ฐ
|
62 |
-
parts.append(
|
63 |
-
types.Part.from_uri(
|
64 |
-
file_uri=person_file.uri,
|
65 |
-
mime_type=person_file.mime_type
|
66 |
-
)
|
67 |
-
)
|
68 |
-
|
69 |
-
# ์ํ ์ด๋ฏธ์ง ์ถ๊ฐ
|
70 |
-
parts.append(
|
71 |
-
types.Part.from_uri(
|
72 |
-
file_uri=product_file.uri,
|
73 |
-
mime_type=product_file.mime_type
|
74 |
-
)
|
75 |
-
)
|
76 |
-
|
77 |
-
# ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง๊ฐ ์์ผ๋ฉด ์ถ๊ฐ
|
78 |
-
if background_img_path:
|
79 |
-
background_file = client.files.upload(file=background_img_path)
|
80 |
-
logger.debug(f"๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์
๋ก๋ ์๋ฃ: {background_file.uri}")
|
81 |
-
parts.append(
|
82 |
-
types.Part.from_uri(
|
83 |
-
file_uri=background_file.uri,
|
84 |
-
mime_type=background_file.mime_type
|
85 |
-
)
|
86 |
-
)
|
87 |
-
logger.debug("๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์ถ๊ฐ๋จ")
|
88 |
-
|
89 |
-
# ๋ง์ง๋ง์ ํ๋กฌํํธ ์ถ๊ฐ
|
90 |
-
parts.append(
|
91 |
types.Part.from_text(text=prompt)
|
92 |
-
|
93 |
|
94 |
logger.debug(f"์ปจํ
์ธ ๊ฐ์ฒด ์์ฑ ์๋ฃ: {len(parts)} ์์ดํ
")
|
95 |
|
@@ -152,7 +116,7 @@ def merge_images(person_img_path, product_img_path, background_img_path, prompt,
|
|
152 |
|
153 |
if not image_saved:
|
154 |
logger.warning("์ด๋ฏธ์ง๊ฐ ์์ฑ๋์ง ์์์ต๋๋ค.")
|
155 |
-
return None, response_text or "์ด๋ฏธ์ง๊ฐ ์์ฑ๋์ง ์์์ต๋๋ค. ๋ค๋ฅธ
|
156 |
|
157 |
logger.debug("์ด๋ฏธ์ง ์์ฑ ์๋ฃ.")
|
158 |
return temp_path, response_text
|
@@ -162,69 +126,17 @@ def merge_images(person_img_path, product_img_path, background_img_path, prompt,
|
|
162 |
return None, f"์ค๋ฅ ๋ฐ์: {str(e)}" # ์ค๋ฅ ๋ฐ์ ์ None๊ณผ ์ค๋ฅ ๋ฉ์์ง ๋ฐํ
|
163 |
|
164 |
|
165 |
-
def
|
166 |
-
logger.debug(f"
|
167 |
try:
|
168 |
-
#
|
169 |
-
if person_pil is None:
|
170 |
-
return
|
171 |
-
if product_pil is None:
|
172 |
-
return [], "์ํ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํด์ฃผ์ธ์."
|
173 |
-
|
174 |
-
# ๊ธฐ๋ณธ ํ๋กฌํํธ ์ค์ (๋น์ด์๋ ๊ฒฝ์ฐ)
|
175 |
-
if not prompt or not prompt.strip():
|
176 |
-
if background_pil:
|
177 |
-
prompt = "์ด ๋ฐฐ๊ฒฝ์ ์ด ์ฌ๋์ด ์ด ์ํ์ ์ฌ์ฉํ๋ ๋ชจ์ต์ ์์ฐ์ค๋ฝ๊ฒ ๋ณด์ฌ์ฃผ์ธ์. ์ํ์ ์ ๋ณด์ด๊ฒ ํด์ฃผ์ธ์. Create a natural composite image showing this person using this product in this background setting. Make sure the product is clearly visible."
|
178 |
-
else:
|
179 |
-
prompt = "์ด ์ฌ๋์ด ์ด ์ํ์ ์ฌ์ฉํ๋ ๋ชจ์ต์ ์์ฐ์ค๋ฝ๊ฒ ๋ณด์ฌ์ฃผ์ธ์. ์ํ์ ์ ๋ณด์ด๊ฒ ํด์ฃผ์ธ์. Create a natural composite image showing this person using this product. Make sure the product is clearly visible."
|
180 |
|
181 |
-
#
|
182 |
-
|
183 |
-
if background_pil:
|
184 |
-
prompt += " Create a realistic composite image of this person with this product in this background."
|
185 |
-
else:
|
186 |
-
prompt += " Create a realistic composite image of this person with this product."
|
187 |
-
|
188 |
-
# ์ด๋ฏธ์ง ์ ์ฅ
|
189 |
-
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_person:
|
190 |
-
person_path = tmp_person.name
|
191 |
-
# RGB๋ก ๋ณํํ์ฌ ์ ์ฅ
|
192 |
-
person_img = person_pil
|
193 |
-
if person_img.mode != 'RGB':
|
194 |
-
person_img = person_img.convert('RGB')
|
195 |
-
person_img.save(person_path, 'JPEG')
|
196 |
-
logger.debug(f"์ฌ๋ ์ด๋ฏธ์ง ์ ์ฅ ์๋ฃ: {person_path}")
|
197 |
-
|
198 |
-
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_product:
|
199 |
-
product_path = tmp_product.name
|
200 |
-
# RGB๋ก ๋ณํํ์ฌ ์ ์ฅ
|
201 |
-
product_img = product_pil
|
202 |
-
if product_img.mode != 'RGB':
|
203 |
-
product_img = product_img.convert('RGB')
|
204 |
-
product_img.save(product_path, 'JPEG')
|
205 |
-
logger.debug(f"์ํ ์ด๋ฏธ์ง ์ ์ฅ ์๋ฃ: {product_path}")
|
206 |
-
|
207 |
-
# ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์ ์ฅ (์๋ ๊ฒฝ์ฐ)
|
208 |
-
background_path = None
|
209 |
-
if background_pil is not None:
|
210 |
-
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_bg:
|
211 |
-
background_path = tmp_bg.name
|
212 |
-
# RGB๋ก ๋ณํํ์ฌ ์ ์ฅ
|
213 |
-
background_img = background_pil
|
214 |
-
if background_img.mode != 'RGB':
|
215 |
-
background_img = background_img.convert('RGB')
|
216 |
-
background_img.save(background_path, 'JPEG')
|
217 |
-
logger.debug(f"๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์ ์ฅ ์๋ฃ: {background_path}")
|
218 |
-
|
219 |
-
# ์ด๋ฏธ์ง ํฉ์ฑ ์คํ
|
220 |
-
result_path, response_text = merge_images(
|
221 |
-
person_img_path=person_path,
|
222 |
-
product_img_path=product_path,
|
223 |
-
background_img_path=background_path,
|
224 |
-
prompt=prompt
|
225 |
-
)
|
226 |
|
227 |
-
# ์ด๋ฏธ์ง ๋ฐํ ๋ฐ
|
228 |
if result_path:
|
229 |
logger.debug(f"์ด๋ฏธ์ง ์์ฑ ์๋ฃ. ๊ฒฝ๋ก: {result_path}")
|
230 |
try:
|
@@ -232,29 +144,20 @@ def process_images_and_prompt(person_pil, product_pil, background_pil, prompt):
|
|
232 |
if result_img.mode == "RGBA":
|
233 |
result_img = result_img.convert("RGB")
|
234 |
|
235 |
-
# ์์ ํ์ผ ์ ๋ฆฌ
|
236 |
-
try:
|
237 |
-
os.unlink(person_path)
|
238 |
-
os.unlink(product_path)
|
239 |
-
if background_path:
|
240 |
-
os.unlink(background_path)
|
241 |
-
except Exception as e:
|
242 |
-
logger.warning(f"์์ ํ์ผ ์ญ์ ์ค ์ค๋ฅ: {str(e)}")
|
243 |
-
|
244 |
return [result_img], response_text or "์ด๋ฏธ์ง๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋์์ต๋๋ค."
|
245 |
except Exception as e:
|
246 |
logger.exception(f"๊ฒฐ๊ณผ ์ด๋ฏธ์ง ๋ก๋ ์ค ์ค๋ฅ: {str(e)}")
|
247 |
return [], f"๊ฒฐ๊ณผ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ์ค ์ค๋ฅ: {str(e)}"
|
248 |
else:
|
249 |
-
logger.error("
|
250 |
-
return [], response_text or "์ด๋ฏธ์ง ์์ฑ์ ์คํจํ์ต๋๋ค. ๋ค๋ฅธ
|
251 |
|
252 |
except Exception as e:
|
253 |
-
logger.exception("
|
254 |
return [], f"์ค๋ฅ ๋ฐ์: {str(e)}" # ์ค๋ฅ ์ ๋น ๋ฆฌ์คํธ์ ์ค๋ฅ ๋ฉ์์ง ๋ฐํ
|
255 |
|
256 |
|
257 |
-
# --- Gradio ์ธํฐํ์ด์ค ๊ตฌ์ฑ ---
|
258 |
with gr.Blocks() as demo:
|
259 |
gr.HTML(
|
260 |
"""
|
@@ -263,45 +166,54 @@ with gr.Blocks() as demo:
|
|
263 |
<img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" style="width: 100px; height: 100px;">
|
264 |
</div>
|
265 |
<div>
|
266 |
-
<h1>Gemini๋ฅผ ์ด์ฉํ ์ด๋ฏธ์ง
|
267 |
-
<p>์ฌ๋, ์ํ, ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง๋ฅผ
|
268 |
</div>
|
269 |
</div>
|
270 |
"""
|
271 |
)
|
272 |
-
gr.Markdown("์ฌ๋ ์ด๋ฏธ์ง, ์ํ ์ด๋ฏธ์ง, ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง๋ฅผ
|
273 |
|
274 |
with gr.Row():
|
275 |
with gr.Column():
|
276 |
-
|
277 |
-
|
|
|
278 |
background_input = gr.Image(type="pil", label="๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง (์ ํ ์ฌํญ)", image_mode="RGB")
|
|
|
|
|
279 |
prompt_input = gr.Textbox(
|
280 |
-
lines=
|
281 |
-
placeholder="
|
282 |
-
label="
|
283 |
)
|
284 |
-
submit_btn = gr.Button("์ด๋ฏธ์ง
|
|
|
285 |
with gr.Column():
|
286 |
-
output_gallery = gr.Gallery(label="
|
287 |
output_text = gr.Textbox(label="AI ์๋ต ํ
์คํธ", visible=True)
|
288 |
|
289 |
submit_btn.click(
|
290 |
-
fn=
|
291 |
inputs=[person_input, product_input, background_input, prompt_input],
|
292 |
outputs=[output_gallery, output_text],
|
293 |
)
|
294 |
|
295 |
gr.HTML("""
|
296 |
<div style="margin-top: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 8px;">
|
297 |
-
<h3>์ฌ์ฉ
|
298 |
<ul>
|
299 |
-
<li><strong
|
300 |
-
<li><strong
|
301 |
-
<li><strong
|
302 |
-
<li><strong
|
303 |
-
|
304 |
-
|
|
|
|
|
|
|
|
|
|
|
305 |
</ul>
|
306 |
</div>
|
307 |
""")
|
|
|
27 |
logger.debug(f"ํ์ผ ์ ์ฅ ์๋ฃ: {file_name}")
|
28 |
|
29 |
|
30 |
+
def generate_image_from_prompt(prompt, model="gemini-2.0-flash-exp-image-generation"):
|
31 |
+
logger.debug(f"generate_image_from_prompt ํจ์ ์์ - ํ๋กฌํํธ: '{prompt}'")
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
|
33 |
try:
|
34 |
# API ํค๋ ํ๊ฒฝ๋ณ์์์ ๋ถ๋ฌ์ด
|
|
|
42 |
client = genai.Client(api_key=effective_api_key)
|
43 |
logger.debug("Gemini ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ์๋ฃ.")
|
44 |
|
45 |
+
# ํ๋กฌํํธ ์ฒ๋ฆฌ
|
46 |
+
if not prompt or not prompt.strip():
|
47 |
+
prompt = "A creative and visually appealing image that captures imagination. Use vibrant colors and interesting composition."
|
|
|
|
|
|
|
48 |
|
49 |
+
# ํ๊ตญ์ด ํ๋กฌํํธ์ ์์ด ์ค๋ช
์ถ๊ฐ
|
50 |
+
if not any(ord(c) < 128 for c in prompt):
|
51 |
+
prompt += " Create a highly detailed, visually stunning image that captures the essence of the description."
|
52 |
+
|
53 |
# ์ปจํ
์ธ ๋ฆฌ์คํธ ์์ฑ
|
54 |
+
parts = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
types.Part.from_text(text=prompt)
|
56 |
+
]
|
57 |
|
58 |
logger.debug(f"์ปจํ
์ธ ๊ฐ์ฒด ์์ฑ ์๋ฃ: {len(parts)} ์์ดํ
")
|
59 |
|
|
|
116 |
|
117 |
if not image_saved:
|
118 |
logger.warning("์ด๋ฏธ์ง๊ฐ ์์ฑ๋์ง ์์์ต๋๋ค.")
|
119 |
+
return None, response_text or "์ด๋ฏธ์ง๊ฐ ์์ฑ๋์ง ์์์ต๋๋ค. ๋ค๋ฅธ ํ๋กฌํํธ๋ก ์๋ํด๋ณด์ธ์."
|
120 |
|
121 |
logger.debug("์ด๋ฏธ์ง ์์ฑ ์๋ฃ.")
|
122 |
return temp_path, response_text
|
|
|
126 |
return None, f"์ค๋ฅ ๋ฐ์: {str(e)}" # ์ค๋ฅ ๋ฐ์ ์ None๊ณผ ์ค๋ฅ ๋ฉ์์ง ๋ฐํ
|
127 |
|
128 |
|
129 |
+
def process_image_generation(person_pil, product_pil, background_pil, prompt):
|
130 |
+
logger.debug(f"process_image_generation ํจ์ ์์ - ํ๋กฌํํธ: '{prompt}'")
|
131 |
try:
|
132 |
+
# ์ด๋ฏธ์ง๋ค์ด ์ ๊ณต๋์๋ค๋ฉด ๊ธฐ์กด ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌ
|
133 |
+
if person_pil is not None or product_pil is not None or background_pil is not None:
|
134 |
+
return process_images_and_prompt(person_pil, product_pil, background_pil, prompt)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
|
136 |
+
# ์ด๋ฏธ์ง ์์ด ํ๋กฌํํธ๋ง ์๋ ๊ฒฝ์ฐ
|
137 |
+
result_path, response_text = generate_image_from_prompt(prompt)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
|
139 |
+
# ์ด๋ฏธ์ง ๋ฐํ ๋ฐ ์ฒ๋ฆฌ
|
140 |
if result_path:
|
141 |
logger.debug(f"์ด๋ฏธ์ง ์์ฑ ์๋ฃ. ๊ฒฝ๋ก: {result_path}")
|
142 |
try:
|
|
|
144 |
if result_img.mode == "RGBA":
|
145 |
result_img = result_img.convert("RGB")
|
146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
return [result_img], response_text or "์ด๋ฏธ์ง๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋์์ต๋๋ค."
|
148 |
except Exception as e:
|
149 |
logger.exception(f"๊ฒฐ๊ณผ ์ด๋ฏธ์ง ๋ก๋ ์ค ์ค๋ฅ: {str(e)}")
|
150 |
return [], f"๊ฒฐ๊ณผ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ์ค ์ค๋ฅ: {str(e)}"
|
151 |
else:
|
152 |
+
logger.error("generate_image_from_prompt ํจ์์์ None ๋ฐํ๋จ.")
|
153 |
+
return [], response_text or "์ด๋ฏธ์ง ์์ฑ์ ์คํจํ์ต๋๋ค. ๋ค๋ฅธ ํ๋กฌํํธ๋ก ์๋ํด๋ณด์ธ์."
|
154 |
|
155 |
except Exception as e:
|
156 |
+
logger.exception("process_image_generation ํจ์์์ ์ค๋ฅ ๋ฐ์:")
|
157 |
return [], f"์ค๋ฅ ๋ฐ์: {str(e)}" # ์ค๋ฅ ์ ๋น ๋ฆฌ์คํธ์ ์ค๋ฅ ๋ฉ์์ง ๋ฐํ
|
158 |
|
159 |
|
160 |
+
# --- Gradio ์ธํฐํ์ด์ค ๊ตฌ์ฑ (๊ธฐ์กด ์ฝ๋๋ ๋์ผํ๊ฒ ์ ์ง) ---
|
161 |
with gr.Blocks() as demo:
|
162 |
gr.HTML(
|
163 |
"""
|
|
|
166 |
<img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" style="width: 100px; height: 100px;">
|
167 |
</div>
|
168 |
<div>
|
169 |
+
<h1>Gemini๋ฅผ ์ด์ฉํ ์ด๋ฏธ์ง ์์ฑ</h1>
|
170 |
+
<p>์ฌ๋, ์ํ, ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง๋ฅผ ํฉ์ฑํ๊ฑฐ๋ ํ
์คํธ๋ก ์ด๋ฏธ์ง๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.</p>
|
171 |
</div>
|
172 |
</div>
|
173 |
"""
|
174 |
)
|
175 |
+
gr.Markdown("์ฌ๋ ์ด๋ฏธ์ง, ์ํ ์ด๋ฏธ์ง, ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ๊ฑฐ๋ ํ
์คํธ๋ก ์ด๋ฏธ์ง๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.")
|
176 |
|
177 |
with gr.Row():
|
178 |
with gr.Column():
|
179 |
+
# ์ด๋ฏธ์ง ์
๋ ฅ ์น์
(์ ํ ์ฌํญ)
|
180 |
+
person_input = gr.Image(type="pil", label="์ฌ๋ ์ด๋ฏธ์ง (์ ํ ์ฌํญ)", image_mode="RGB")
|
181 |
+
product_input = gr.Image(type="pil", label="์ํ ์ด๋ฏธ์ง (์ ํ ์ฌํญ)", image_mode="RGB")
|
182 |
background_input = gr.Image(type="pil", label="๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง (์ ํ ์ฌํญ)", image_mode="RGB")
|
183 |
+
|
184 |
+
# ํ๋กฌํํธ ์
๋ ฅ (ํ์)
|
185 |
prompt_input = gr.Textbox(
|
186 |
+
lines=3,
|
187 |
+
placeholder="์ด๋ฏธ์ง ์์ฑ ์ค๋ช
์ ์
๋ ฅํด์ฃผ์ธ์. ์: '๊ฝ๋ฐญ์์ ํ๋น์ ๋ฐ์ผ๋ฉฐ ๋ฏธ์ ์ง๋ ์ ์ ์ฌ์ฑ', 'ํ๋์ ์ธ ๊ฑฐ์ค์์ ๊ณ ๊ธ ์ค๋งํธํฐ์ ์ฌ์ฉํ๋ ๋น์ฆ๋์ค๋งจ'",
|
188 |
+
label="์ด๋ฏธ์ง ์์ฑ ์ค๋ช
(ํ์)"
|
189 |
)
|
190 |
+
submit_btn = gr.Button("์ด๋ฏธ์ง ์์ฑ")
|
191 |
+
|
192 |
with gr.Column():
|
193 |
+
output_gallery = gr.Gallery(label="์์ฑ๋ ์ด๋ฏธ์ง")
|
194 |
output_text = gr.Textbox(label="AI ์๋ต ํ
์คํธ", visible=True)
|
195 |
|
196 |
submit_btn.click(
|
197 |
+
fn=process_image_generation,
|
198 |
inputs=[person_input, product_input, background_input, prompt_input],
|
199 |
outputs=[output_gallery, output_text],
|
200 |
)
|
201 |
|
202 |
gr.HTML("""
|
203 |
<div style="margin-top: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 8px;">
|
204 |
+
<h3>์ฌ์ฉ ๋ฐฉ๋ฒ:</h3>
|
205 |
<ul>
|
206 |
+
<li><strong>ํ
์คํธ๋ก ์ด๋ฏธ์ง ์์ฑ:</strong> ์ค๋ช
๋ง ์
๋ ฅํ๊ณ ์ด๋ฏธ์ง ์์ฑ ๋ฒํผ์ ๋๋ฅด์ธ์.</li>
|
207 |
+
<li><strong>์ด๋ฏธ์ง ํฉ์ฑ:</strong> ์ฌ๋, ์ํ, ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง๋ฅผ ์ ํ์ ์ผ๋ก ์
๋ก๋ํ๊ณ ํฉ์ฑ ๋ฐฉ๋ฒ์ ์ค๋ช
ํด์ฃผ์ธ์.</li>
|
208 |
+
<li><strong>ํ๋กฌํํธ ํ:</strong> ๊ตฌ์ฒด์ ์ด๊ณ ์์ํ ์ค๋ช
์ผ์๋ก ๋ ์ข์ ์ด๋ฏธ์ง๊ฐ ์์ฑ๋ฉ๋๋ค.</li>
|
209 |
+
<li><strong>์์ ํ๋กฌํํธ:</strong>
|
210 |
+
<ul>
|
211 |
+
<li>ํ์ ํ ์นดํ์์ ๋
ธํธ๋ถ์ผ๋ก ์์
ํ๋ ํฌ๋ฆฌ์์ดํฐ</li>
|
212 |
+
<li>ํด๋ณ๊ฐ์์ ๊ณ ๊ธ ์ ๊ธ๋ผ์ค๋ฅผ ๋ผ๊ณ ํฌ์ฆ ์ทจํ๋ ๋ชจ๋ธ</li>
|
213 |
+
<li>ํ๋์ ์ธ ์ฃผ๋ฐฉ์์ ์ต์ ๋ธ๋ ๋๋ก ์ค๋ฌด๋๋ฅผ ๋ง๋๋ ์๋ฆฌ์ฌ</li>
|
214 |
+
</ul>
|
215 |
+
</li>
|
216 |
+
<li><strong>์ธ์ด ํ:</strong> ํ๊ตญ์ด์ ์์ด๋ฅผ ํผํฉํด ์ฌ์ฉํ๋ฉด ๋ ์ ํํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค.</li>
|
217 |
</ul>
|
218 |
</div>
|
219 |
""")
|