4 minute read

โ€œ@Backend OO๋‹˜, ํž™ ๋คํ”„ ๋œฐ ์ค„ ์•Œ์ฃ ?โ€

When servers experience unexplained crashes or performance drops, often due to memory leaks, capturing heap dumps is essential for diagnosis. This guide explains how to generate heap dumps in Spring Boot applications.

๋“ค์–ด๊ฐ€๋ฉฐโ€ฆ

์„œ๋ฒ„๊ฐ€ ์•Œ ์ˆ˜ ์—†๋Š” ์ด์œ ๋กœ ๊ฐ‘์ž๊ธฐ ์ฃฝ๊ฑฐ๋‚˜ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ข…์ข… ์žˆ๋Š”๋ฐ,

๋ณดํ†ต heap memory ๋‚ด์—์„œ memory leak ์ด ์›์ธ์ธ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

๋ณด๋‹ค ์ •๋ฐ€ํ•˜๊ฒŒ ๋ถ„์„ํ•ด์„œ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด heap dump ๋ฅผ ๋– ์„œ ํ™•์ธํ•ด๋ณด์•„์•ผ ํ•˜๋Š”๋ฐ,

์ด์ „์— ๊ด€๋ จ ๊ฒฝํ—˜์ด ์—†๋‹ค๋ฉด heapdump ๋ฅผ ๋œจ๋‹ค. ๋ผ๋Š” ๋ง ์ž์ฒด๊ฐ€ ์ƒ์†Œํ•  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.

๋ณธ ํฌ์ŠคํŒ…์—์„œ heapdump ๋ฅผ ๋– ๋ณด๋Š” ํ–‰์œ„ ๋ฅผ ํ•œ ์‚ฌ์ดํด ํ•ด๋ณด๋ฉด์„œ.. ๊ฐ€๋ณ๊ฒŒ ๋Š๋‚Œ์„ ์žก์•„๋ณด๋„๋ก ํ•˜์ž.

๋‹นํ™ฉํ•˜์ง€ ์•Š์„ ๋ฏธ๋ž˜์˜ ์ž์‹ ์„ ์œ„ํ•ด..!


์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ๋กœ ํ•œ๋ฒˆ ์‹ค์Šตํ•ด๋ณด์ž~!

ํ˜„์žฌ ์ง„ํ–‰์ค‘์ธ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” Spring boot application ์€ jib tool ์„ ์ด์šฉํ•˜์—ฌ, docker container ํ™˜๊ฒฝ์—์„œ ๋ฐฐํฌํ•˜๊ณ  ์žˆ๋‹ค.

์ด์™€ ๊ฐ™์€ ํ™˜๊ฒฝ์—์„œ heap dump ๋Š” ์–ด๋–ป๊ฒŒ ์ƒ์„ฑํ•˜๋Š” ์ง€ ์ง€๊ธˆ ๋ฐ”๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ์•Œ์•„๋ด…์‹œ๋‹ค!~

Heap-dump

heap dump ๋ถ„์„์„ ํ†ตํ•ด Java application ์˜ ์„ฑ๋Šฅ ๋ถ„์„์„ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ heap dump ์ด๋ž€, heap memory ์— ๋Œ€ํ•œ snapshot ์œผ๋กœ

memory leak ์ด๋‚˜ ๊ธฐํƒ€ ๋‹ค๋ฅธ ๋ฌธ์ œ๋ฅผ ์ง„๋‹จํ•˜๋Š” ๋ฐ์— ํฐ ๋‹จ์„œ๋ฅผ ์ œ๊ณตํ•ด์ค๋‹ˆ๋‹ค.

์ด์ „์— ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ ์ž๊พธ ec2 ์„œ๋ฒ„๊ฐ€ OOM (OutOfMemory) ์—๋Ÿฌ ๋กœ๊ทธ๋ฅผ ๋„์šฐ๋ฉฐ ์ฃฝ๋Š” ๊ฒฝํ—˜์„ ํ–ˆ์—ˆ๋Š”๋ฐ

๊ทธ ๋‹น์‹œ์— heap dump ์— ๋Œ€ํ•ด์„œ ์•Œ๊ณ  ์žˆ์—ˆ๋”๋ผ๋ฉด

์กฐ๊ธˆ ๋” ์‰ฝ๊ฒŒ ๋ฌธ์ œ๋ฅผ ์ง„๋‹จํ•˜๊ณ  ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์•˜์„๊นŒ.. ํ•˜๋Š” ์•„์‰ฌ์›€์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌํŠผ, heap dump ๋ฅผ ํ™œ์šฉํ•˜๋ฉด memory leak ์— ์˜ํ•œ ์„œ๋ฒ„ ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ํฌ๊ฒŒ ๋‹นํ™ฉํ•˜์ง€ ์•Š๊ณ  ์›์ธ์„ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ OutOfMemory ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ์‚ฌ์ „์— ์ง€์ •ํ•ด๋‘” path ์— heap dump ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๋„๋ก ํ•ด๋‘๋Š” ๊ฒƒ์ด ๋งˆ์Œ ํŽธํ•œ ๋ฐฉ๋ฒ•์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด์„œ JVM option ์„ ์ฃผ๋ฉด ๋˜๋Š”๋ฐ,

์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” jib ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ

์•„๋ž˜์™€ ๊ฐ™์ด jvmFlag ๋ฅผ ์ง€์ •ํ•ด์„œ, OOM ๋ฐœ์ƒ ์‹œ ์ง€์ •๋œ ๊ฒฝ๋กœ์— heapdump ๊ฐ€ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ์ž์˜ ํ”„๋กœ์ ํŠธ ํ™˜๊ฒฝ์— ๋งž์ถฐ์„œ ์„ค์ •ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ฐธ๊ณ ์šฉ์œผ๋กœ๋งŒ ๋ณด๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค!

jib {
	from {
		image = "openjdk:17"
	}
	to {
		image = "your docker image"
		tags = ["latest"]
	}
	container {
		...
		jvmFlags = [ ... your jvm option ... ,  "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/your-path/your-heap-dump-file-name.hprof"]
		...
	}
}

docker file ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ํ•ด์ค„ ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”!

FROM openjdk:17
...
CMD ["java", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/path/to/heapdump.hprof", "-jar", "myapp.jar"]

์‚ฌ์‹ค ์ด๋ ‡๊ฒŒํ•˜๋ฉด heapdump ์˜ ํŒŒ์ผ๊ฒฝ๋กœ๊ฐ€ ์–ธ์ œ๋‚˜ ๋™์ผํ•˜๋ฏ€๋กœ, overwrite ๋œ๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋ณดํ†ต์€ ๋‚ ์งœ๋ฅผ ์ด์šฉํ•ด์„œ, OOM ์ด ๋ฐœ์ƒํ•œ ์‹œ์ ์˜ ๋‚ ์งœ๋กœ heapdump ํŒŒ์ผ์˜ ์ด๋ฆ„์„ ์ง€์ •ํ•ด์„œ

heapdump ํŒŒ์ผ๋“ค์˜ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ œ๊ฐ€ ์šด์šฉ์ค‘์ธ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ์˜ ec2 ์‚ฌ์–‘์€ t2.micro ์ด๊ณ ..

heapdump ํŒŒ์ผ์˜ ์šฉ๋Ÿ‰์ด ์ž‘์ง€ ์•Š์€ ํŽธ์ด๋ฏ€๋กœ

๋งค๋ฒˆ overwrite ๋˜๋„๋ก ์„ค์ •ํ•ด๋‘์—ˆ์Šต๋‹ˆ๋‹ค.

์—ฌํŠผ, ์œ„์™€ ๊ฐ™์ด JVM ์˜ต์…˜์„ ์ฃผ๋ฉด

OOM ์ด ๋ฐœ์ƒํ•˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์ง€์ •ํ•ด๋‘” ๊ฒฝ๋กœ์— heapdump ํŒŒ์ผ์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

heapdump ํŒŒ์ผ์€ ๋ณดํ†ต eclips memory analysis tool(MAT) ๋ฅผ ์ด์šฉํ•ด์„œ ์‹œ๊ฐ์ ์œผ๋กœ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


Heap dump ์ˆ˜๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ธฐ

์œ„์™€ ๊ฐ™์ด jvm option ์„ ์ฃผ๋ฉด OOM ๋ฐœ์ƒ ์‹œ heap dump ํŒŒ์ผ์ด ์ง€์ •๋œ ๊ฒฝ๋กœ์— ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ, ์ˆ˜๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ• ๊นŒ์š”?

๋ณดํ†ต jmap ์„ ์ด์šฉํ•˜์—ฌ heap dump ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, java app ์˜ pid ๊ฐ€ 1111 ์ด๋ผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์—ฌ heapdump file ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

jmap -dump:live,format=b,file=heapdump.hprof 1111
  • jmap ๋ช…๋ น์–ด๋Š” ์‹คํ–‰์ค‘์ธ java process ์— ๋ถ€ํ•˜๋ฅผ ์ค„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, production ์—์„œ๋Š” ์ฃผ์˜ํ•ด์ฃผ์„ธ์š”!
  • ๋งŒ์•ฝ jmap ๋ช…๋ น์–ด๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์—๋Š” jcmd ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ heapdump ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • jcmd 1111 GC.heap_dump heapdump.hprof
    • ํ™•์ธํ•ด๋ณด๋‹ˆ jmap ๋ณด๋‹ค jcmd ๊ฐ€ ์กฐ๊ธˆ ๋” ์„ฑ๋Šฅ์ ์œผ๋กœ ์šฐ์ˆ˜ํ•˜๋‹ค๊ณ  ํ•˜๋‹ˆ, jcmd ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ ๊ฐ™๋„ค์š”!

java app ์ด docker container ๋‚ด์—์„œ ๋™์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋” ์ผ๋ฐ˜์ ์ผํ…๋ฐ,

์ด๋•Œ๋Š” ํ•ด๋‹น container ๋‚ด๋ถ€์— ๋จผ์ € ์ ‘๊ทผํ•ด์•ผ๊ฒ ์ฃ ..!

docker ps

๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์‹คํ–‰์ค‘์ธ Spring app ์˜ container id ๋ฅผ ์•Œ์•„๋‚ธ ๋‹ค์Œ

docker exec -it [your-container-id] bash

๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์ปจํ…Œ์ด๋„ˆ์— bash ํ„ฐ๋ฏธ๋„๋กœ ๋ถ™์–ด์ค์‹œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ์ปจํ…Œ์ด๋„ˆ์—์„œ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋Š” java app ์˜ pid ๋ฅผ ์•Œ์•„๋‚ด์•ผ ํ•˜๋Š”๋ฐ์š”,

ps -ef | grep java

๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด heapdump ๋ฅผ ๋œจ๊ณ ์ž ํ•˜๋Š” java app ์˜ pid ๋ฅผ ์–ป์–ด๋ƒ…์‹œ๋‹ค.

๋งŒ์•ฝ ํ•ด๋‹น ์ปจํ…Œ์ด๋„ˆ์—์„œ ps ๋ช…๋ น์–ด๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด (์‚ฌ์‹ค ๋Œ€๋ถ€๋ถ„ baseimage ๊ฐ€ jdk ์ผํ…Œ๋‹ˆ.. ์ง€์› ์•ˆํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์„ ๊ฒƒ ๊ฐ™๋„ค์š”.)

jps -l

๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์‹คํ–‰์ค‘์ธ java app ์˜ pid ๋ฆฌ์ŠคํŠธ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


container to ec2-host

๋„์ปค ์ปจํ…Œ์ด๋„ˆ ๋‚ด์— ์ƒ์„ฑ๋œ heapdump file ์„ host ๋กœ ์˜ฎ๊ฒจ์•ผ๊ฒ ์ฃ !?

์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

docker cp mycontainer:/heapdump.hprof to-directory

์˜ˆ๋ฅผ ๋“ค์–ด

docker cp mycontainer:/heapdump.hprof .

๋ผ๊ณ  ์ž…๋ ฅํ•œ ๋’ค์— exit ๋ฅผ ํ†ตํ•ด container ๋ฐ”๊นฅ์œผ๋กœ ๋‚˜๊ฐ€๊ณ  root directory ๋กœ ๊ฐ€๋ณด๋ฉด heapdump.hprof ํŒŒ์ผ์ด ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค!

ec2-host to local

์ด์ œ ec2 ์— ์žˆ๋Š” heapdump ํŒŒ์ผ์„ ๋กœ์ปฌ ์ปดํ“จํ„ฐ๋กœ ์˜ฎ๊ฒจ์ฃผ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋ณดํ†ต scp ๋ช…๋ น์–ด๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

scp -i [your-pem-key-file-path] [your-ec2-user-name]@[your-ec2-ip-address]:[heapdump-file-path] [to-file-path]

๋งŒ์•ฝ, ์œ„ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋„์ค‘์—

scp: remote open "/heapdump.hprof": Permission denied

์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด

heapdump.hprof ์˜ ๊ถŒํ•œ์„ ํ™•์ธํ•˜๊ณ  ํ•„์š” ์‹œ ์กฐ์ •ํ•ด์ค์‹œ๋‹ค.

์˜ˆ์‹œ๋กœ,

์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด heapdump.hprof ์— ๋Œ€ํ•œ rw ๊ถŒํ•œ์„ ๋ชจ๋“  ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

sudo chmod 644 /path/to/heapdump.hprof

์ด์ œ ๋‹ค์‹œ ํŒŒ์ผ ์ „์†ก์„ ํ•˜๋ฉด ์›ํ™œํ•˜๊ฒŒ ๋  ๊ฒƒ ์ž…๋‹ˆ๋‹ค ใ…Ž_ใ…Ž

์•„, ๊ทธ๋Ÿฐ๋ฐ ํ•˜๋‚˜ ๊ถ๊ธˆ์ฆ์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์•„์š”.

์ง€๊ธˆ ์šฐ๋ฆฌ๋Š” ๋„์ปค ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์— ์ƒ์„ฑ๋œ ํž™ ๋คํ”„๋ฅผ ๋กœ์ปฌ์— ์˜ฎ๊ธฐ๋ ค๊ณ  ec2 ๋ฅผ ์ค‘๊ฐ„ ๋‹ค๋ฆฌ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ฃ ..?

๋„์ปค ์ปจํ…Œ์ด๋„ˆ์—์„œ ๋กœ์ปฌ๋กœ ๋ฐ”๋กœ ์ „์†กํ•˜์ง€ ์•Š๊ณ , ์™œ ec2 ์— ๊ฐ”๋‹ค๊ฐ€ ๋กœ์ปฌ๋กœ ์ „์†กํ•˜๊ณ  ์žˆ๋Š”๊ฑธ๊นŒ์š”?

๋„์ปค์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์›์น™์ด ๋ฐ”๋กœ ๊ฒฉ๋ฆฌ์„ฑ์ด์ฃ !?

host system ์„ ๊ฑฐ์ณ์„œ local ์œผ๋กœ ํŒŒ์ผ์„ ์ „์†กํ•˜๋Š” ๊ฒƒ์€ ๋„์ปค์˜ ๊ธฐ๋ณธ ์›์น™์ธ ๊ฒฉ๋ฆฌ์„ฑ์„ ์ค€์ˆ˜ํ•˜๋ฉด์„œ๋„,

๋ณด์•ˆ์ ์œผ๋กœ ์•ˆ์ „ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋„์ปค ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์™ธ๋ถ€ ๋„คํŠธ์›Œํฌ์— ๋…ธ์ถœ์‹œํ‚ค๋Š” ๊ฒƒ์€ ๊ฒฉ๋ฆฌ์„ฑ๋„ ์œ„๋ฐฐํ•˜๊ณ , ๋ณด์•ˆ์ ์œผ๋กœ ์ข‹์ง€ ์•Š์•„์š”.

eclipse-MAT

์ด์ œ ์ƒ์„ฑ๋œ heapdump ํŒŒ์ผ์„ eclipse-MAT ์œผ๋กœ ์—ด์–ด๋ด…์‹œ๋‹ค~!

image

image


Java heapdump ๋ถ„์„์„ ํ†ตํ•ด, Java app ์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ณ  memory leak ๋“ฑ์˜ ์น˜๋ช…์ ์ธ ๋ฌธ์ œ๋ฅผ ์ง„๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณธ ํฌ์ŠคํŒ…์—์„œ๋Š” ๊ฐ„๋‹จํžˆ heapdump ๊ฐ€ ๋ฌด์—‡์ธ์ง€, ์–ด๋–ป๊ฒŒ ์ƒ์„ฑํ•˜๊ณ  ์—ด์–ด๋ณด๋Š”์ง€๋ฅผ ํ™•์ธํ•ด๋ณด์•˜๋Š”๋ฐ์š”

์‹œ๊ฐ„์ด ๋ถ€์กฑํ•œ ๊ด€๊ณ„๋กœโ€ฆ ๐Ÿฅฒ

heapdump ๋ถ„์„์— ๊ด€ํ•ด์„œ๋Š” ํ›„์†๊ธ€์—์„œ ๊ณ„์† ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ˜น์‹œ ์ž˜๋ชป๋œ ๋ถ€๋ถ„์„ ๋ฐœ๊ฒฌํ•˜์‹ ๋‹ค๋ฉด ์ฝ”๋ฉ˜ํŠธ ๋‚จ๊ฒจ์ฃผ์„ธ์š”! ๐Ÿ™‡โ€โ™‚๏ธ

Leave a comment