백오피스 파일 오퍼레이션 코드 개선기

DY·2023년 5월 22일
0

현황

내가 담당하는 업무는 쇼핑몰 백오피스 운영이고 고객에게 노출될 이미지, 동영상 등 파일들을 업로드 하는 로직이 여러 메뉴에 존재한다.
예를 들면, 기획전 배너 이미지, 광고 배너, 공지 배너 등등... 다양한 종류의 이미지들을 업로드 하고 있으며
이 이미지들은 홈쇼핑의 FTP 서버에 업로드 하고 있다. 업로드 타겟이 되는 FTP 서버의 유형은 2가지이다.
이미지와 동영상만 언급했지만, 업로드 대상이 되는 파일의 유형은 PDF파일이 될 수도 있으며 HTML 컨텐츠를 업로드 할 수도 있다.

FTP에 연결하고 업로드 하는 핵심 로직은 특정 클래스(아래 코드의 SftpOperationExt)에 구현되어 있으며,
업로드가 필요한 각 메뉴별 서비스에서 대략 아래와 같은 메서드를 만들고, 실제 이미지를 업로드 하는 메서드 내에서 호출하고 있다.

예시 코드
private void uplaodImageBySftp(InputStream is, String imageName, String remotePath) throws Exception {
	SftpOperationExt sftp = null;
    try {
    	sftp = (SftpOperationExt)sftpOperationFactory.connect();	// get SFTP connection
        sftp.uploadAsStream(is, imageName, new File(remotePath)).disconnect();
    } catch(Exception e) {
    	throw e;
    } finally {
    	if (sftp != null && sftp.isConnected()) {
        	try {
            	sftp.disconnect();
            } catch (Exception e) {
            	// Logging exception message
            }
        }
    }
}

문제점

사실 위의 예시 코드는 그 동안 별 문제 없이 새로운 기능 혹은 메뉴가 추가될 때 마다 관행처럼 복붙하던 코드였으나, 연내 파일 업로드 타겟이 FTP 서버에서 AWS S3로 전환될 예정으로 이슈가 되는 코드로 바뀌게 되었다. 위와 같은 코드를 모두 찾아 바꿔야 했기 때문이다.

그리하여 FTP 업로드를 수행하는 코드들에 대한 전수조사 중, 아래와 같은 추가적인 문제를 발견하게 되었다.

  • 파일을 업로드 하는 것 뿐 아니라 다운로드, 삭제, 복사가 이루어지는 경우도 있음. 이를 모두 포함하여 파일 오퍼레이션으로 칭함
  • 파일 오퍼레이션이 발생하는 메뉴와 그 메뉴에서 이루어지는 파일 오퍼레이션의 유형, 파일 경로 등이 각 메뉴별 서비스 소스에 흩어져 있어 한 눈에 파악이 어려운 구조
  • 파일 오퍼레이션을 수행하는 위 예시 코드와 함께 업로드 대상 FTP 서버를 선택하는 코드라든지, 업로드 경로를 생성하는 코드 등 추가적으로 발생하는 코드들이 생각보다 많이 존재하고 이는 비즈니스 로직 코드 내에 섞여있음
  • 업로드(다운로드, 삭제, 복사 등의 경우도 포함) 경로의 경우 서비스 소스에 하드코딩 되어있는 경우도 있었으며, 환경별 프로퍼티 파일에 정의되어 있는 경우도 많았음

위의 내용들을 종합하여 최종적으로 정리한 현재 애플리케이션 내 FTP 처리 로직의 문제점은 아래와 같았다.

  • FTP 오퍼레이션에 대해 중복되는 코드가 너무 많다.
    이미 핵심 코드가 있음에도 불구하고 각 서비스마다 이를 사용하기 위한 메서드를 추가로 구현하고 있기 때문에 서비스 내에 불필요한 코드가 많아지게 된다.
    불필요한 코드는 위에서 언급한 추가적으로 발생하는 코드들도 포함된다. 추가적인 코드들로 인해 서비스 소스는 해당 기능의 로직에 온전히 집중하기 어렵게 된다.
  • 어느 메뉴에서 어떤 유형의 파일이 어느 위치에 저장되는지에 대한 정보를 관리하는 포인트가 없다.
    파일 오퍼레이션에 대한 이슈가 있어 확인이 필요할 때 마다 특정 키워드로 검색하고 소스를 하나하나 확인하는 과정을 거쳐야만 한다.
  • 환경별 프로퍼티 파일에 환경에 영향을 받지 않는 업로드 경로 값들이 너무 많다.
    로컬/개발/운영 환경별로 달라지지 않을 값이 굳이 환경별 프로퍼티 파일에 존재해야 할 이유가 없다.
  • 지금과 같이 파일 오퍼레이션 로직에 이슈가 생긴다면 일일이 찾아 바꿔줘야 한다.
    극단적으로 S3에 업로드하는 것에서 또 다른 방식으로 업로드 방식이 바뀌어야 한다면, 수많은 소스들을 찾아 또 다시 일일이 바꿔줘야 하며, 지금과 같다면 전수조사를 또 해야만 한다.

개선방안

그래서 나는 아래와 같은 점을 핵심으로 삼아 개선을 진행하기로 했다.

  • 파일 오퍼레이션을 위한 공통 인터페이스를 만들고, 기존에 메서드를 직접 구현하던 서비스들은 이 인터페이스를 통해 파일 오퍼레이션을 수행한다.
  • 파일 오퍼레이션 인터페이스에는 각 메뉴들에서 수행하던 업로드, 복사, 다운로드, 삭제 기능을 정의한다.
  • 기존 IDC FTP 관련 로직, 그리고 AWS S3 관련 로직들을 이용하여 파일 오퍼레이션 인터페이스의 구현체를 만든다.
  • S3 이전 전에는 FTP 오퍼레이션 구현체를 @Primary로 지정하며, S3 이전 후에는 S3 오퍼레이션 구현체를 @Primary로 지정한다.
    각 메뉴의 서비스에서는 파일 오퍼레이션의 우선순위 및 상세 로직에 대해 신경쓰지 않도록 한다.
  • 파일 오퍼레이션이 이루어지는 메뉴 및 영역, 파일 유형에 대해서는 Enum으로 정의하도록 한다. 이를 Category로 명명한다.
  • Category에 대해 IDC, S3 환경별 실제 파일 업로드 경로에 대한 Enum을 작성하도록 한다.
    Category에 정의된 name에 대해 각 환경(FTP, S3)별 Enum에도 값이 정의되어 있어야 한다.
    파일 오퍼레이션 시 Category Enum 및 각 환경별 Enum을 참조하여 작업을 수행할 것이다.

개선내역

위와 같은 개선 방안으로 최종 개선한 내역은 아래와 같다.

1. 파일 오퍼레이션 인터페이스(FileOperation)
interface FileOperation {
	void upload(Category category, InputStream is);
    void download(Category category, String destination);
    void copy(String origin, String target);	// 예외적으로 Category를 따르지 않고 위치를 직접 지정
    void delete(Category category, String filename);
    String getUploadPath(Category category, Map<String, String> pathParameter);
}

기존 애플리케이션에서 발생하는 파일 오퍼레이션 4종류에 대해 인터페이스 내에 정의하고, 추가적인 코드 중 대표적인 사례였던 파일 오퍼레이션 경로 반환 또한 파일 오퍼레이션 구현체에서 수행하도록 했다.
Category는 파일 오퍼레이션이 발생하는 메뉴 및 파일 유형에 대한 정의이며, Enum이다. 파일 오퍼레이션이 발생하는 로직 내에서는 직접 파일 오퍼레이션 경로를 만드는 과정을 코드로 작성하지 않고(파일 복사의 경우 예외), 어느 영역에서 발생하는 파일 오퍼레이션인지에 대한 정보만 전달한다.

2. 파일 오퍼레이션 발생 메뉴 및 파일 유형 정의(Category)
enum Category {
	DEAL_IMAGE(FileType.IMAGE),
    DEAL_VIDEO(FileType.VIDEO),
    ADVERTISE_BANNER(FileType.IMAGE),
    ...
}

파일 오퍼레이션이 이루어지는 영역에 대한 정의이다. 각 서비스 로직에서는 파일 오퍼레이션 시 Category에 정의된 이름을 통해 요청한다. 해당 영역에 대한 파일 유형 등 환경별로 달라지지 않는 값은 위와 같이 함께 정의한다.

3. Category에 대한 환경별 정의({env}Category)
enum FtpCategory {
	DEAL_IMAGE(ServerType.IMAGE, "/deal/image"),
    DEAL_VIDEO(ServerType.VIDEO, "/deal/video"),
    ADVERTISE_BANNER(ServerType.IMAGE, "/banner/{YYYYMM}"),
    ...
}

enum S3Category {
	DEAL_IMAGE("/IMG/deal/image"),
    DEAL_VIDEO("/VOD/deal/video"),
    ADVERTISE_BANNER("/IMG/banner/{YYYYMM}"),
    ...
}

Category에 대한 상세 내용을 정의한 enum이다. 파일 오퍼레이션 경로, 오퍼레이션 대상 FTP 서버 등 환경에 따라 달라질 수 있는 값들을 정의하기 위한 것이다. 위 2개의 enum을 보면 서로 경로가 다른 것을 볼 수 있을 것이다. 파일 오퍼레이션 인터페이스 구현체에서는 Category에 정의된 이름을 이용하여 각 환경에 맞는 enum에 정의된 이름의 값을 찾아 작업을 수행한다.

4. 파일 오퍼레이션 인터페이스 구현체({env}FileOperation)
@Primary
public class FtpFileOperation implements FileOperation {
	@Override
    public void upload(Category category, InputStream is) {
    	FtpCategory categoryDetail = FtpCategory.valueOf(category.name());	// FtpCategory 참조
        ...	// FTP 파일 업로드 로직
    }
}

개선 이전에도 사용하던 로직 기반으로 파일 오퍼레이션 구현체를 만든다. 단, 기존에는 각 서비스 소스 상황에 따라 조금씩 다를 수 있었지만 이제 공통적으로 사용 가능하도록 코드를 작성한다. 환경에 따라 알맞는 Category 상세 enum을 참조한다.
(FtpFileOperation은 FtpCategory를 참조, S3FileOperation은 S3Category를 참조)

5. 실제로 파일 오퍼레이션을 수행하는 서비스 소스들
@Autowired
private FileOperation fileOperation;
...
// 업로드 수행 - DEAL_IMAGE 유형에 file 업로드 시
fileOperation.upload(Category.DEAL_IMAGE, file.getInputStream);
...
// 결과 처리 시 업로드 경로가 필요하다면 아래와 같이 획득, 경로 내 파라미터가 없으므로 null 전달
fileOperation.getUploadPath(Category.DEAL_IMAGE, null);
...

이제 각 서비스 소스 내에서는 이 글의 초반에 언급했던 예시 코드와 같은 코드를 작성할 필요가 없다. 경로를 만들어내는 코드도 작성하지 않아도 되며, 환경별 프로퍼티 파일에 경로를 기입할 필요도 없다. 필요한 오퍼레이션에 대한 메서드를 호출하는 코드를 넣기만 하면 된다. 다만, 새로운 카테고리가 생긴다면 Category와 환경별 Category 상세 enum에 정의해주면 된다.

효과

사실 상황에 의해서 반강제로 하게 된 것이기도 하지만... 개선 후 실질적으로 느낀 효과는 아래와 같았다.

  • 필요할 때 모든 영역의 파일 오퍼레이션 타겟을 쉽게 변경 가능.
    모든 코드를 찾아 바꿔야 했던 기존과 달리 의존성 주입의 대상이 되는 구현체만 변경하면 되기 때문에 좀 더 유연한 구조가 된 것 같다.
    구현체는 Spring Bean 형태로 존재하므로, 필요에 따라 메뉴별로 다른 타겟에 파일 오퍼레이션을 하도록 하는 것도 가능하다.
  • 서비스 소스 내 파일 오퍼레이션에 대한, 서비스 로직과 관련 없는 불필요한 코드 대폭 감소.
    이 문서 상단의 예시 코드와 같은 것들이 모두 사라지고 파일 오퍼레이션 구현체에 딱 하나만 존재하게 되었다.
    파일 오퍼레이션이 필요한 기능을 추가할 때 마다 불필요하게 관련 코드를 복붙할 필요가 없어졌고, 구현할 로직에만 집중하면 된다.
  • 파일 오퍼레이션이 발생하는 영역 60곳에 대해 현황 파악이 용이해짐.
    실제로 개선 작업 이후 특정 FTP 서버에 업로드하는 메뉴 및 경로 파악 요청이 있었는데, 이전에 비해 매우 짧은 시간 내 현황에 대해 파악하고 공유하는 것이 가능했다.

그밖에 아쉬운 점과 우려도 약간은 남아있었는데...

  • 파일 오퍼레이션이 발생하는 영역을 정의한 Category를 작성하고, 각 환경별로 달라지는 값을 별도로 정의하게 했는데 이 과정이 너무 번거롭다.
    만약 파일 업로드 되는 영역이 새로 하나 추가된다고 하면, Category 정의 및 각 환경별 값 정의(여기서는 FTP 및 S3 총 2가지) 총 3개의 Enum 파일에 내용을 추가해 주어야 한다. 실수로 누락할 가능성도 있는 부분이다.
  • 기존의 방식보다 직관적이지는 않다. 이 코드를 모르는 사람이 봤을 때 파악하는 것이 기존 코드보다는 어려울 것이라는 이야기이다. 전형적으로 아는 사람만 아는 코드가 될 수 있어서 이 부분은 문서화를 잘 해두고 같은 업무 수행자들에게 공유하는 것이 필요할 것 같다.

마무리하며

사실 간단하게 보면 기존에 흩어져 있던 코드들을 모아서 한곳에 모아서 그것을 가져다 쓰도록 한 것에 불과하지만, 실제로는 파일 오퍼레이션 환경에 대한 대응도 어렵지 않게 되었고 파일 오퍼레이션의 발생에 대한 포인트를 한 곳에서 관리함으로써 운영하는 입장에서는 더 편리해진 느낌이 들었다. 또한, 생각없이 복사-붙여넣기 하던 코드들이 더 이상 필요하지 않게 되었으며 이에 따라 불필요하게 중복되는 코드들이 제거되어 각 서비스는 자신의 비즈니스 로직에만 집중할 수 있게 되었다.
물론 이번과 같은 경우는 꼭 변화가 이루어져야만 하는 상황이긴 했지만, 이번 작업을 진행하면서 당연하게 생각하던 것에 대해 가끔은 의구심을 갖고 바라보는 것의 중요성과, 그것에 대해 가볍게나마 개선해 나가는 것이 업무 효율에 큰 영향을 미친다는 것을 다시 한 번 깨닫게 되었다.

0개의 댓글

관련 채용 정보