⌨️ 주니어의 관점에서?
최근 신규 기능을 추가하고 기존 기능들을 개선하면서 배우거나 생각해 볼 만한 것들에 대해서 정리를 했다.
일전의 모 유튜브에서 이런 말을 본 적이 있다.
입문 수준의 개발자들은 기능을 구현하기에 급급해서 코드의 품질이 떨어진다.
이전에는 이 말이 단순히 코드의 가독성이나 짜임새를 말하는 줄 알았는데, 최근에서야 어떤 시야가 트이게 됐다.
혼자서 느낀 건 아니고 지속적으로 통찰점을 제시해주는 동료분이 있어서 가능했다고 생각한다.
1. PDF 파일 생성 후 메일 발송 기능
우선 간단히 제한 사항에 대해서 말해보자면
1. 클라이언트에서 한번에 대용량의 파일이 전송된다.
2. 서버에 데이터를 저장할 필요는 없으며 메일 발송 시 어차피 byte []로 다시 넣어준다.
3. 서버의 성능은 그리 좋지 못하다.
나는 2번에 대한 걸 놓쳐서 실수를 했다. 기능 구현은 했지만 퀄리티가 떨어진다는 것이 무슨 말인지 알 것 같은 느낌이었다.
대략적인 구현을 보자
1. 1. DataUriString (Base64)
@RestController
public class PdfController {
@PostMapping("/upload-base64")
public String uploadBase64(@RequestBody Map<String, String> request) {
String dataUri = request.get("dataUri");
// Base64 부분 추출
String base64Data = dataUri.split(",")[1];
// Base64 디코딩
byte[] pdfBytes = Base64.getDecoder().decode(base64Data);
// 파일로 저장
try (OutputStream os = new FileOutputStream("uploaded_base64.pdf")) {
os.write(pdfBytes);
return "파일 저장 성공 (Base64)";
} catch (IOException e) {
e.printStackTrace();
return "파일 저장 실패 (Base64)";
}
}
}
1.2. Binary Data
@RestController
public class PdfController {
@PostMapping("/upload-binary")
public String uploadBinary(@RequestBody byte[] fileBytes) {
// 파일로 저장
try {
Files.write(Paths.get("uploaded_binary.pdf"), fileBytes);
return "파일 저장 성공 (바이너리)";
} catch (IOException e) {
e.printStackTrace();
return "파일 저장 실패 (바이너리)";
}
}
}
문제점
아까 말했듯이 서버에 데이터는 저장될 필요가 없고 MailSender는 Multipart를 전송할 때 attach를 file을 받는다.
해당 파라미터는 byte [] 타입도 가능하다.
즉 내가 맨 처음 구현한 Base64를 활용한 방식은
1. 받은 Base64 String을 Decoding 하면서 String 할당
2. 해당 String을 getByte() 넣어줄 byte [] 할당
즉 byte[]로 바로 보낼 수 있는 것을 굳이 decode() 하는 과정을 거치는 불필요한 작업이 발생한다.
물론 모종의 이유로 Base64를 선택했지만.. 좋지 못한 선택이었다는 것을 알았다.
일단 우리의 서버는 대용량의 메일발송을 위한 대용량 메일 첨부 구현이 안되어있기도 했고
그 정도 용량의 첨부파일을 보내는 상황에서 때에 따라 Out of Memory가 발생하면서 서버가 죽기도 했다
사실 후자의 이유가 크다.
사용자들이 빈번히 서버가 느려진다고 요청이 들어오기에 메일 발송의 속도와 용이성을 포기해서라도 서버의 퍼포먼스를 유지해야 했다
2. 장 -단점
2.1. Base64 인코딩 된 datauristring을 사용하여 PDF 데이터를 전송
장점
- 텍스트 전송: Base64 인코딩된 데이터는 텍스트 형식이므로, JSON과 함께 쉽게 포함할 수 있다. 이는 여러 데이터를 한 번에 전송할 때 유용하며 메타데이터를 담을 수 있다..
- CORS 문제 해결: 일부 브라우저 환경에서 CORS 문제가 발생할 수 있는 경우, Base64로 인코딩하면 이를 우회할 수 있다.
- 데이터 무결성: Base64는 데이터가 손상될 가능성을 줄여준다다. 이는 네트워크 전송 중에 데이터가 변조되거나 손상되는 것을 방지하는 데 유리.
단점
- 크기 증가: Base64 인코딩은 데이터 크기를 약 30% 증가. 이는 네트워크 대역폭과 저장 공간을 더 많이 사용하게 만듦 ( 하지만 대부분 서버 증설 비용이 더 비쌈..)
- 인코딩/디코딩 비용: 클라이언트와 서버에서 추가적인 인코딩 및 디코딩 처리가 필요하므로, 처리 시간이 더 길어짊.
- 메모리 사용량: 큰 파일을 Base64로 인코딩하면 메모리 사용량이 증가할 수 있다 -> 위에서 말한 Decoding 과정 등으로 인해
2.2. 바이너리 데이터를 직접 전송
장점
- 효율성: 바이너리 데이터는 원본 크기를 그대로 유지하므로, 네트워크 대역폭과 저장 공간을 효율적으로 사용할 수 있다. -> 하지만 멀티파트 제한이 굉장히 작다면 청크로 보내거나 소량의 데이터만 전송하도록 유도해야 함
- 단순성: 인코딩과 디코딩 과정이 없으므로, 처리 시간이 더 짧고 코드가 단순해짐.
- 성능: 대용량 데이터를 전송할 때, 바이너리 형식은 더 나은 성능을 제공.
단점
- 복잡한 설정: 바이너리 데이터를 전송하기 위해 클라이언트와 서버 모두에서 적절한 설정이 필요. 특히, application/octet-stream 타입을 처리하는 데 신경 써야 함.
- 호환성 문제: 일부 API나 시스템에서 바이너리 데이터를 처리하기 어렵거나, 특정 브라우저 환경에서 문제가 발생할 수 있습니다 -> 요새는 문제 있는 브라우저는 없음
- CORS 문제: 바이너리 데이터를 전송할 때, 특히 CORS 설정이 복잡할 수 있다.
2.3. 요약
기준 Base64 인코딩 된 datauristring바이너리 데이터 직접 전송
장점 | 텍스트 전송, CORS 문제 해결, 데이터 무결성 보장 | 효율성, 단순성, 성능 향상 |
단점 | 크기 증가, 인코딩/디코딩 비용, 메모리 사용량 증가 | 복잡한 설정, 호환성 문제, CORS 문제 발생 가능 |
3. 피드백
분명히 내가 경험해 본 이슈기도 하고, 해결도 같은 방식으로 했었다.
그러나 실제 기능을 개발할 때, 다른 코드 그리고 이미 짜인 구성에 대해 새로운 기능 추가하는 부분에서 이런 부분들을 간과하게 되었다.
이유가 뭘까 생각해 봤는데,
기능을 구현하기 급급해서 그런 것 같다. 혹은 내가 해본 것을 응용하거나 떠오르기 어려워서 인 것 같기도 하다.
이런 것들을 계기로 파편화된 지식들을 조금은 유기적으로 활용할 수 있도록 고민해 봐야겠다.
여러분은 어떤가? 분명해봤고 잘 안다고 생각했는데, 그것의 활용이 만족할 만큼 이루어지는가?
신입 개발자로서 한 번쯤 위가 아닌 아래를 바라보며,
내가 쌓아온 것들이 견고히 나를 지탱해 주는지 확인해볼 가치가 있는 것 같다.