** ๐Ÿ“Œ PART 5.1 ํŒŒ์ผ ์—…๋กœ๋“œ์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ์™€ ์ตœ์ ํ™” ์ „๋žต **

โ€œํŒŒ์ผ์„ ์˜ฌ๋ฆด ๋•Œ ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ„ฐ์ง€์ง€ ์•Š๋„๋ก ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฒ•โ€


๐ŸŸข 1๋‹จ๊ณ„. ํŒŒ์ผ ์—…๋กœ๋“œ๊ฐ€ ๋ญ์•ผ?

๐Ÿ’ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋‚ด ์ปดํ“จํ„ฐ์— ์žˆ๋Š” ์‚ฌ์ง„์ด๋‚˜ ๋ฌธ์„œ๋ฅผ

์›น ์‚ฌ์ดํŠธ์— ์ฒจ๋ถ€ํ•ด์„œ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด๋Š” ๊ธฐ๋Šฅ์ด์•ผ.

<form method="POST" enctype="multipart/form-data">
  <input type="file" name="photo" />
  <input type="submit" value="์—…๋กœ๋“œ" />
</form>

โœ… ์ด๊ฑธ ์„œ๋ฒ„๊ฐ€ ๋ฐ›์œผ๋ ค๋ฉด?


๐Ÿ“ฆ 2๋‹จ๊ณ„. MultipartRequest๋ž€? (cos.jar or commons-fileupload)


โœ… cos.jar ์‚ฌ์šฉ ์˜ˆ์‹œ (๊ฐ€์žฅ ์‰ฌ์›€)

MultipartRequest multi = new MultipartRequest(
   request,
   "C:/upload/",      // ์—…๋กœ๋“œ ๊ฒฝ๋กœ
   10 * 1024 * 1024,  // ์ตœ๋Œ€ ์—…๋กœ๋“œ ์šฉ๋Ÿ‰ (10MB)
   "UTF-8"
);

โœ… ์ฒ˜๋ฆฌ ํ๋ฆ„

1. ํŒŒ์ผ ์—…๋กœ๋“œ ์š”์ฒญ์ด ๋“ค์–ด์˜ด
2. MultipartRequest๊ฐ€ ์š”์ฒญ์„ ํŒŒ์‹ฑํ•จ
3. ํŒŒ์ผ์€ ์„œ๋ฒ„ ํ•˜๋“œ๋””์Šคํฌ(๊ฒฝ๋กœ)์— ์ €์žฅ๋จ
4. ์ผ๋ฐ˜ ํ…์ŠคํŠธ๋Š” ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ฒ˜๋ฆฌ๋จ

๐Ÿ’พ 3๋‹จ๊ณ„. ์ž„์‹œ ํŒŒ์ผ ์ €์žฅ vs ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ ์ฐจ์ด์ 


๊ตฌ๋ถ„ ์„ค๋ช… ๋ฉ”๋ชจ๋ฆฌ ์˜ํ–ฅ
์ž„์‹œ ํŒŒ์ผ ์ €์žฅ ํŒŒ์ผ ๋‚ด์šฉ์„ ์„œ๋ฒ„์˜ ํ•˜๋“œ๋””์Šคํฌ์— ์ €์žฅ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ ์Œ โœ…
๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ ์ž‘์€ ํŒŒ์ผ(text, ํ•„๋“œ ๊ฐ’ ๋“ฑ)์€ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ ๋„ˆ๋ฌด ๋งŽ์œผ๋ฉด GC/๋ฉ”๋ชจ๋ฆฌ ํ„ฐ์ง ์œ„ํ—˜ โŒ

โœ… ์—…๋กœ๋“œํ•  ๋•Œ ํŒŒ์ผ์ด ์–ด๋””์— ์ €์žฅ๋ ๊นŒ?


๐Ÿ’ฅ ๋ฌธ์ œ: ํฐ ํŒŒ์ผ์„ ๊ณ„์† ์˜ฌ๋ฆฌ๋ฉด?

์„œ๋ฒ„๋Š” ์š”์ฒญ์„ ํŒŒ์‹ฑํ•˜๊ธฐ ์ „์— ์ „์ฒด ์š”์ฒญ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ฝ๊ฒŒ ๋˜๋Š”๋ฐ,

ํŒŒ์ผ์ด ๋„ˆ๋ฌด ํฌ๊ฑฐ๋‚˜ ๋™์‹œ์— ๋„ˆ๋ฌด ๋งŽ์ด ์˜ฌ๋ผ์˜ค๋ฉด โ†’ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์˜ค๋ฅ˜ ๋ฐœ์ƒ!


๐Ÿ˜ฑ ์˜ค๋ฅ˜ ์˜ˆ์‹œ: java.lang.OutOfMemoryError: Java heap space


๐Ÿง  4๋‹จ๊ณ„. ํฐ ํŒŒ์ผ ์—…๋กœ๋“œ ์‹œ ๋ฉ”๋ชจ๋ฆฌ ํญ๋ฐœ ๋ฐฉ์ง€ ์ „๋žต


โœ… ์ „๋žต 1: ์—…๋กœ๋“œ ํฌ๊ธฐ ์ œํ•œ ์„ค์ •

// ์ตœ๋Œ€ 10MB๊นŒ์ง€ ์—…๋กœ๋“œ ๊ฐ€๋Šฅ
MultipartRequest multi = new MultipartRequest(
   request,
   "C:/upload/",
   10 * 1024 * 1024, // 10MB
   "UTF-8"
);

โœ… ๋„ˆ๋ฌด ํฐ ํŒŒ์ผ์€ ๊ฑฐ๋ถ€ํ•ด์„œ ์„œ๋ฒ„๋ฅผ ์ง€ํ‚ด!


โœ… ์ „๋žต 2: ์—…๋กœ๋“œ ๊ฒฝ๋กœ๋ฅผ ์™ธ๋ถ€ ๋””์Šคํฌ๋กœ ๋ถ„๋ฆฌ


โœ… ์ „๋žต 3: commons-fileupload ์‚ฌ์šฉ + ์ŠคํŠธ๋ฆฌ๋ฐ ์—…๋กœ๋“œ ๋ฐฉ์‹

Apache commons-fileupload๋Š” ์š”์ฒญ์„ ํ•œ ์ค„์”ฉ ์ฝ์œผ๋ฉด์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ

โ†’ ์ „์ฒด ํŒŒ์ผ์„ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ฆฌ์ง€ ์•Š์•„๋„ ๋จ!


โœ… Streaming ๋ฐฉ์‹ ์˜ˆ์‹œ ํ๋ฆ„ (์ค‘์š”)

1. ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด โ†’ InputStream์œผ๋กœ ํ•œ ์ค„์”ฉ ์ฝ๊ธฐ
2. ๋ฉ”๋ชจ๋ฆฌ์— ์Œ“์ง€ ์•Š๊ณ  โ†’ ๋ฐ”๋กœ ํ•˜๋“œ๋””์Šคํฌ์— ์“ฐ๊ธฐ
3. ์šฉ๋Ÿ‰ ์ƒ๊ด€์—†์ด ์•ˆ์ •์ ์œผ๋กœ ์—…๋กœ๋“œ ๊ฐ€๋Šฅ!

โœ… ์ „๋žต 4: Tomcat์˜ ์—…๋กœ๋“œ ์„ค์ • ์ œํ•œํ•˜๊ธฐ (web.xml or server.xml)

<!-- web.xml -->
<multipart-config>
   <max-file-size>10485760</max-file-size> <!-- 10MB -->
   <max-request-size>20971520</max-request-size> <!-- 20MB -->
</multipart-config>

โ†’ ์„œ๋ฒ„ ์ฐจ์›์—์„œ๋„ ๋ฐฉ์–ด!


๐ŸŽ“ 5๋‹จ๊ณ„. ๋ฉด์ ‘ ํฌ์ธํŠธ ์ •๋ฆฌ

์งˆ๋ฌธ ๋ชจ๋ฒ” ๋‹ต๋ณ€ ์š”์•ฝ
MultipartRequest๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋‚˜์š”? ํ…์ŠคํŠธ๋Š” ๋ฉ”๋ชจ๋ฆฌ์—, ํŒŒ์ผ์€ ๋””์Šคํฌ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค
ํฐ ํŒŒ์ผ ์—…๋กœ๋“œ ์‹œ ์™œ OOM์ด ๋ฐœ์ƒํ•˜๋‚˜์š”? ๋ฉ”๋ชจ๋ฆฌ๋กœ ์ „์ฒด ์š”์ฒญ์„ ์ฝ์œผ๋ ค๋‹ค ์šฉ๋Ÿ‰ ์ดˆ๊ณผ
๋ฉ”๋ชจ๋ฆฌ ํญ๋ฐœ ๋ฐฉ์ง€ ์ „๋žต์€? ํฌ๊ธฐ ์ œํ•œ + ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ + ๋””์Šคํฌ ์ €์žฅ ๊ฒฝ๋กœ ๋ถ„๋ฆฌ
cos.jar vs commons-fileupload ์ฐจ์ด๋Š”? cos๋Š” ๊ฐ„๋‹จ, commons๋Š” ์„ฑ๋Šฅ/ํ™•์žฅ์„ฑ ์šฐ์ˆ˜
์—…๋กœ๋“œ ์šฉ๋Ÿ‰ ์„ค์ •์€ ์–ด๋””์„œ ํ•˜๋‚˜์š”? MultipartRequest ์ฝ”๋“œ, web.xml, ๋˜๋Š” Tomcat ์„ค์ •์—์„œ

โœ… ๋งˆ๋ฌด๋ฆฌ ์š”์•ฝํ‘œ

ํ•ญ๋ชฉ ์„ค๋ช… ๋ฉ”๋ชจ๋ฆฌ ํšจ๊ณผ
MultipartRequest ์š”์ฒญ์„ ํŒŒ์‹ฑํ•˜๊ณ  ํŒŒ์ผ ์ €์žฅ ํฐ ํŒŒ์ผ์€ ๋””์Šคํฌ๋กœ ์ฒ˜๋ฆฌ
ํ…์ŠคํŠธ ํ•„๋“œ ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ ๋‹ค๋Ÿ‰ ์š”์ฒญ ์‹œ GC ๋ถ€๋‹ด โ†‘
์—…๋กœ๋“œ ์ œํ•œ max size ์„ค์ • OOM ๋ฐฉ์ง€, ์„ฑ๋Šฅ ์•ˆ์ •
์ŠคํŠธ๋ฆฌ๋ฐ ์—…๋กœ๋“œ InputStream ์ฒ˜๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ๊ฑฐ์˜ ์—†์Œ โœ…

โœ… PART 5.2 ๋‹ค์šด๋กœ๋“œ ์‹œ ํŒŒ์ผ ์ŠคํŠธ๋ฆผ ๋ฒ„ํผ๋ง ์ „๋žต

โ€œํŒŒ์ผ์„ ๋น ๋ฅด๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ์ „์†กํ•˜๋ฉด์„œ ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ๋„ ๋ณดํ˜ธํ•˜๋Š” ๋ฒ•โ€


๐ŸŸข 1๋‹จ๊ณ„. ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ๊ตฌ์กฐ๋ถ€ํ„ฐ ์ดํ•ดํ•˜์ž

๐Ÿ’ฌ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ๋ž€, ์„œ๋ฒ„์— ์ €์žฅ๋œ ํŒŒ์ผ์„

๋ธŒ๋ผ์šฐ์ €์— ์ „์†กํ•ด์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ธฐ๋Šฅ์ด์•ผ.


โœ… ํ๋ฆ„ ์š”์•ฝ

1. ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ํด๋ฆญ
2. ์„œ๋ฒ„๊ฐ€ ํŒŒ์ผ์„ ์ฝ์Œ (InputStream)
3. ์‚ฌ์šฉ์ž์—๊ฒŒ ์ „์†กํ•จ (OutputStream)

๐Ÿ“ฆ JSP/Servlet ๋‹ค์šด๋กœ๋“œ ๊ธฐ๋ณธ ์ฝ”๋“œ ์˜ˆ์‹œ

File file = new File("C:/upload/sample.pdf");
FileInputStream in = new FileInputStream(file);
OutputStream out = response.getOutputStream();

byte[] buffer = new byte[4096];
int length;

while ((length = in.read(buffer)) != -1) {
    out.write(buffer, 0, length);
}

๐Ÿ” 2๋‹จ๊ณ„. Stream ์ง์ ‘ ์ฒ˜๋ฆฌ vs ByteBuffer ์ฐจ์ด์ 


โœ… ๋ฐฉ์‹ 1: Stream ์ง์ ‘ ์ฒ˜๋ฆฌ (์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ)

์žฅ์ 


โœ… ๋ฐฉ์‹ 2: ByteBuffer ์‚ฌ์šฉ (NIO ๊ธฐ๋ฐ˜ ๊ณ ์„ฑ๋Šฅ ๋ฐฉ์‹)

Path path = Paths.get("C:/upload/sample.pdf");
ByteBuffer buffer = ByteBuffer.allocate(8192); // 8KB

try (SeekableByteChannel channel = Files.newByteChannel(path)) {
    while (channel.read(buffer) > 0) {
        buffer.flip();
        out.write(buffer.array(), 0, buffer.limit());
        buffer.clear();
    }
}

์žฅ์ 


โœ… ์„ฑ๋Šฅ/๋ฉ”๋ชจ๋ฆฌ ๋น„๊ต ์š”์•ฝํ‘œ

ํ•ญ๋ชฉ Stream ๋ฐฉ์‹ ByteBuffer(NIO) ๋ฐฉ์‹
์†๋„ ์ค‘๊ฐ„ ๋น ๋ฆ„ (NIO ์ตœ์ ํ™”)
์ฝ”๋“œ ๊ฐ„๋‹จ์„ฑ ๐Ÿ‘ ๋งค์šฐ ์‰ฌ์›€ ์„ค์ • ๋ณต์žก
๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ ๋ณดํ†ต (heap ์‚ฌ์šฉ) ๋†’์Œ (heap ๋ฐ–์—์„œ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ)
์‹ค๋ฌด ์‚ฌ์šฉ ์ผ๋ฐ˜ ์›น์—์„œ๋Š” Stream ๋Œ€์šฉ๋Ÿ‰/๊ณ ์„ฑ๋Šฅ ์‹œ์Šคํ…œ์—์„œ NIO ์‚ฌ์šฉ

๐Ÿ’พ 3๋‹จ๊ณ„. flush() / close()์˜ ์—ญํ• ๊ณผ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€ ์ „๋žต


โœ… flush()

๐Ÿ’ฌ ๋ฒ„ํผ์— ์Œ“์ธ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ๋‚ด๋ณด๋‚ด๋Š” ์—ญํ• 

  • write()๋งŒ ํ•˜๋ฉด ์‹ค์ œ ์ „์†ก ์•ˆ ๋  ์ˆ˜ ์žˆ์Œ
  • flush()๋ฅผ ํ•ด์ค˜์•ผ โ†’ ๋„คํŠธ์›Œํฌ๋กœ ์ „์†ก๋จ!
out.flush(); // ๐Ÿ’ก ์ด๊ฑฐ ๊ผญ ํ•ด์ค˜์•ผ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํŒŒ์ผ์„ ์™„์ „ํžˆ ๋ฐ›์Œ!

โœ… close()

๐Ÿ’ฌ ํŒŒ์ผ ์ŠคํŠธ๋ฆผ์„ ์™„์ „ํžˆ ๋‹ซ๊ณ , ๋ฆฌ์†Œ์Šค๋ฅผ ํ•ด์ œํ•˜๋Š” ๋ฉ”์„œ๋“œ

  • ์•ˆ ํ•˜๋ฉด โ†’ ์ŠคํŠธ๋ฆผ์ด ๊ณ„์† ์‚ด์•„์žˆ์–ด์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐœ์ƒ
  • ํŠนํžˆ ํŒŒ์ผ ์ŠคํŠธ๋ฆผ์€ OS ์ž์›์„ ์ ์œ ํ•จ!
in.close();
out.close();

๐Ÿง  ์‹ค๋ฌด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€ ํ•ต์‹ฌ ์ „๋žต

์ „๋žต ์„ค๋ช…
try-with-resources ์‚ฌ์šฉ ์ž๋™์œผ๋กœ close() ํ˜ธ์ถœ๋จ
flush() โ†’ close() ์ˆœ์„œ ์ง€ํ‚ค๊ธฐ ์•ˆ ํ•˜๋ฉด ํŒŒ์ผ ์ผ๋ถ€ ๋ˆ„๋ฝ ๊ฐ€๋Šฅ
๋Œ€์šฉ๋Ÿ‰์€ ๋ฐ˜๋“œ์‹œ buffer ์‚ฌ์šฉ ๋ฒ„ํผ ์—†์ด ์ฒ˜๋ฆฌ ์‹œ ๋ฉ”๋ชจ๋ฆฌ ํญ์ฃผ ๊ฐ€๋Šฅ
๋กœ๊ทธ ์ถœ๋ ฅ ์ œํ•œ ๋‹ค์šด๋กœ๋“œ ์ค‘ ๋„ˆ๋ฌด ๋งŽ์€ ๋กœ๊ทธ ์ถœ๋ ฅ์€ GC ์••๋ฐ• ๋ฐœ์ƒ

โœ… try-with-resources ์‚ฌ์šฉ ์˜ˆ์‹œ (์ถ”์ฒœ ๋ฐฉ์‹)

try (
    FileInputStream in = new FileInputStream(file);
    OutputStream out = response.getOutputStream();
) {
    byte[] buffer = new byte[8192];
    int len;
    while ((len = in.read(buffer)) != -1) {
        out.write(buffer, 0, len);
    }
    out.flush();
} catch (Exception e) {
    e.printStackTrace();
}

โ†’ ์ž๋™์œผ๋กœ close() ํ˜ธ์ถœ๋จ โ†’ ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „ โœ…


๐ŸŽ“ 4๋‹จ๊ณ„. ๋ฉด์ ‘ ํฌ์ธํŠธ ์ •๋ฆฌ

์งˆ๋ฌธ ๋ชจ๋ฒ” ๋‹ต๋ณ€ ์š”์•ฝ
ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ๋Š” ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋‚˜์š”? InputStream์œผ๋กœ ์ฝ๊ณ , OutputStream์œผ๋กœ ํด๋ผ์ด์–ธํŠธ์— ์ „์†กํ•ฉ๋‹ˆ๋‹ค
ByteBuffer๋Š” ์–ธ์ œ ์“ฐ๋‚˜์š”? ๊ณ ์„ฑ๋Šฅ, ๋Œ€์šฉ๋Ÿ‰ ๋‹ค์šด๋กœ๋“œ์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค (NIO ๊ธฐ๋ฐ˜)
flush()๋Š” ์™œ ํ•„์š”ํ•˜๋‚˜์š”? ๋ฒ„ํผ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋„คํŠธ์›Œํฌ๋กœ ๊ฐ•์ œ ์ „์†กํ•ฉ๋‹ˆ๋‹ค
close() ์•ˆ ํ•˜๋ฉด ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋‚˜์š”? ํŒŒ์ผ ํ•ธ๋“ค์ด ์—ด๋ ค ์žˆ์–ด GC๊ฐ€ ์ •๋ฆฌ ๋ชป ํ•˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐœ์ƒ
try-with-resources์˜ ์žฅ์ ์€? close ๋ˆ„๋ฝ ์—†์ด ์ž๋™ ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ โ†’ ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „์„ฑ โ†‘

โœ… ๋งˆ๋ฌด๋ฆฌ ์š”์•ฝํ‘œ

ํ•ญ๋ชฉ ์„ค๋ช… ๋ฉ”๋ชจ๋ฆฌ ์˜ํ–ฅ
Stream ๋ฐฉ์‹ ์ผ๋ฐ˜ ๋‹ค์šด๋กœ๋“œ ์ฒ˜๋ฆฌ buffer ํฌ๊ธฐ๋งŒํผ๋งŒ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ
ByteBuffer ๋ฐฉ์‹ ๊ณ ์„ฑ๋Šฅ NIO ๊ธฐ๋ฐ˜ direct memory ํ™œ์šฉ์œผ๋กœ GC ๋ถ€๋‹ด โ†“
flush() ๋ฒ„ํผ โ†’ ํด๋ผ์ด์–ธํŠธ ์ „์†ก ์ „์†ก ๋ˆ„๋ฝ ๋ฐฉ์ง€
close() ์ž์› ํ•ด์ œ ์•ˆ ํ•˜๋ฉด ๋ˆ„์ˆ˜ ๋ฐœ์ƒ
try-with-resources ์ž๋™ close ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ •์„ฑ ์ตœ๊ณ  ๐Ÿ‘