올리브영 테크블로그 포스팅 상품 설명 영역 개선기 Part.1
Product

상품 설명 영역 개선기 Part.1

코루틴을 사용하여 상품 설명 영역 이미지 타입 개선하기

2024.04.01

안녕하세요. 상품 스쿼드의 백엔드 개발자 벙개맨⚡️입니다.

여러분은 올리브영에서 상품을 구매하실 때 상품 설명을 확인하시나요?

고객에게 상품에 대한 정보나 꿀팁을 제공하여 뽐뿌를 불러오는 상품 설명은 상품을 구매하는데 꼭 필요한 정보인데요!

기존에 어떤 부분이 문제였고, 상품 설명 개선을 통하여 어떻게 개선되었는지 차근차근 설명드리겠습니다!

image1


상품 설명(상세 기술서)란?


먼저 저희 올리브영에서는 상품 설명상세 기술서라고 호칭하고 있습니다.

상세 기술서란 상품 상세 페이지 내에 위치하여 제품에 대한 자세한 설명이 포함된 문서를 말합니다.

제품의 기술적인 세부 정보, 제조사 정보, 사용 방법, 주의 사항 등이 포함될 수 있고,

소비자가 제품을 구매하기 전에 제품의 성능, 기능, 재료 등을 이해할 수 있도록 도와주는 역할을 하고 있습니다.



image2

저희 올리브영에서는 상세 기술서를 HTML 타입이미지 타입 2가지로 나누어서 사용하고 있는데요.

HTML 타입은 기본 이미지(jpg, png) 외에도 gif, video 등 다양한 타입의 확장자를 입력할 수 있고,

이미지만 간단하게 등록할 경우 이미지 타입으로 등록하여 사용하고 있습니다.

작년에 상세 기술서 이미지 타입 개선에 이어서 HTML 타입에 대한 개선도 진행되었는데요.

먼저 Part.1에서 이미지 타입에 대하여 어떠한 부분이 개선되었는지부터 설명해 드리고,

Part.2에서 HTML 타입 개선 내용에 대하여 설명드리겠습니다!


이미지 타입의 상세 기술서 무엇이 문제였나요?


이미지 업로드 AS-IS Process 구성도

image3

기존 이미지 타입의 상세 기술서를 처리할 때 이미지 파일을 NAS 서버에 업로드하고 있었는데,

NAS는 물리적 서버기 때문에 용량이 늘어나면 서버 증설 작업이 필요하여 사용을 지양하고 있습니다.

또 복잡하게 구성된 레거시 소스를 사용하고 있어서 유지 보수가 어려운 상태였고,

백포스 또는 신규 파트너 오피스에서 상세 기술서 이미지를 등록할 때 동일한 기능의 코드가 별도로 구성되어 있어서

수정이 필요할 경우 이중으로 코드를 수정해야 하는 문제도 있었습니다.


image4

이미지 타입의 상세 기술서는 어떻게 개선되었나요?


이미지 업로드 및 상세 기술서 데이터 처리에 대한 공통 API를 개발하여,

백포스, 신규 파트너 오피스에서 동일한 API 호출을 통하여 데이터가 처리되도록 구성하였습니다.

또 NAS 서버에 업로드하던 이미지 파일을 AWS S3에 업로드 되도록 개선하였고,

이미지 파일 업로드 시 코루틴을 사용한 비동기 처리로 구성하여 처리 속도에 대한 개선도 진행하였습니다.

이미지 업로드 TO-BE Process 구성도

image5

Step 1) BackOffice, New PartnerOffice에서 상품 등록, 수정을 통하여 상세 기술서 이미지 변경

Step 2) Product Api에 신규로 개발된 상세 기술서 이미지 처리 Api 호출

  • QMS 서버 및 AWS S3에 이미지 파일 업로드
  • Oracle DB 상세 이미지 데이터 처리

Step 3) Response를 받아서 Oracle DB 상품 기본 정보 데이터 처리


이렇게 간단하면서 유지 보수가 용이한 구조로 개선되었고

상세 기술서 이미지 외에 대표 이미지 등 상품에 사용되는 이미지들도 추후 AWS S3로 이관 진행할 예정입니다!

어떤 점이 개선되었나요?

  • 기술서 이미지를 NAS 서버에 업로드하는 구조에서 AWS S3로 업로드 되도록 변경하여 확장성 있는 구조로 개선
  • 백포스, 신규 파트너오피스에서 상세 기술서 이미지 처리 시 공통 로직을 사용하여 유지 보수 용이
  • GS-CDN에서 제공하는 webp 확장자 변환 기능을 사용하여 고객에게 더욱 빠르게 상세 기술서 제공



💁‍♂️ webp란?

Google에서 개발한 이미지 파일 형식으로 웹 페이지에서 이미지를 더 빠르게 조회하고 작은 파일 크기로 유지하기 위해 설계되었습니다.

이 형식은 손실 압축 및 무손실 압축을 지원하며 JPEG와 PNG 파일 형식보다 더 작은 파일 크기를 가질 수 있습니다.

원본

webp



📚 마지막으로 상세 기술서 개선 과정에서 빼놓을 수 없었던 코루틴에 대하여 설명을 드리고 마무리하겠습니다.

코루틴 이란?

Coroutine = co + routine 협력 + 작업

코루틴(Coroutine)은 together를 뜻하는 co와 Routine이 합쳐져 만들어진 단어이고,

일종의 가벼운 스레드(Light-weifgt thread)로 동시성 작업을 간편하게 처리할 수 있게 해주는 역할을 합니다.

image6

코루틴의 장점

  • 경량성: 코루틴은 스레드보다 훨씬 가볍습니다. 스레드는 운영체제에 의해 관리되고, 스레드 간의 전환은 오버헤드가 큽니다. 그러나 코루틴은 단일 스레드 내에서 실행되기 때문에 스레드 간의 전환 비용이 없습니다.

  • 공유 상태 문제 해결: 멀티스레딩에서는 공유 자원에 대한 접근 제어가 필요합니다. 하지만 코루틴은 단일 스레드 내에서 실행되므로 공유 상태 문제를 다루기가 더 쉽습니다.

  • 쉬운 동시성 제어: 코루틴은 코드를 동시에 실행할 수 있도록 돕는데, 이는 비동기 작업을 처리할 때 매우 유용합니다. 예를 들어 네트워크 요청이나 파일 I/O와 같은 작업을 병렬로 처리할 수 있습니다.

  • 가독성: 코루틴은 코드를 순차적으로 작성할 수 있도록 해줍니다. 비동기 작업을 처리할 때도 코드가 순차적으로 흐르기 때문에 가독성이 향상됩니다.


코루틴은 어떻게 사용하나요?

코루틴을 사용하기 위해 먼저 Coroutine Context, Coroutine Dispatcher, Coroutine Scope, Coroutine Builder에 대하여 간단하게 설명드리겠습니다.

  • Coroutine Context 코루틴이 실행될 때 해당 코루틴이 동작하는 환경을 정의합니다.

  • Coroutine Dispatcher 작업을 위해 생성된 코루틴을 스레드에 할당하는 역할을 합니다. 종류로는 Main, Default, IO가 있고, 각 특징은 다음과 같습니다.

    • Dispatchers.Main UI 작업을 위한 Main Thread로 이동시키는 코루틴 디스패처로 UI와 상호작용하는 작업을 실행할 경우 사용합니다.

    • Dispatchers.Default CPU를 많이 사용하는 작업을 Main Thread가 아닌 다른 Thread에서 CPU를 많이 사용하는 작업을 할 경우 사용합니다.

    • Dispatchers.IO 네트워크/DB 등 입출력이 있는 작업을 할 경우 사용합니다.

  • Coroutine Scope 코루틴이 실행되는 범위로, 코루틴을 실행하고 싶은 Lifecycle에 따라 원하는 Scope를 생성하여 코루틴이 실행될 작업 범위를 지정할 수 있습니다.

  • Coroutine Builder 코루틴을 실행할 때 사용하는 여러 가지 함수를 뜻하며 종류로는 **launch, async, runBlocking, withContext가 있고 각 특징은 다음과 같습니다.

    • launch 비동기적으로 새로운 코루틴을 시작합니다. 반환 값이 없으며, 실행된 코루틴이 완료될 때까지 기다리지 않습니다.

    • async 비동기적으로 결과를 반환하는 코루틴을 시작합니다. Deferred 타입의 값을 반환하며, 이를 통해 나중에 결과를 얻을 수 있습니다.

    • runBlocking 동기적인 블록 내에서 새로운 코루틴을 시작합니다. 주로 테스트나 메인 함수에서 사용되며, 일반적인 코드 흐름을 변경하지 않고 코루틴을 사용할 수 있게 합니다.

    • withContext 현재 코루틴의 컨텍스트를 변경하여 새로운 코루틴을 시작합니다. 주로 다른 스레드에서 실행되어야 하는 작업을 지정된 디스패처에서 실행할 때 사용됩니다.


상세 기술서 개선 작업을 진행하면서 작성한 코드를 보면,

suspend fun uploadS3MultipleFilesWithCoroutine(
    imageUploads: List<ImageUploadVo>
): List<PutObjectResult> = withContext(Dispatchers.IO) {

    try {
        val uploadJobs = imageUploads.map { imageUploadVo ->
            val objectMetadata = ObjectMetadata().apply {
                contentType = imageUploadVo.fileExtension
                contentLength = imageUploadVo.file.size
            }

            async {
                val putObjectRequest = PutObjectRequest(
                    "$productBucket$detailImagePath",
                    imageUploadVo.filePath,
                    imageUploadVo.file.inputStream,
                    objectMetadata
                )
                s3Client.putObject(putObjectRequest)
            }
        }
        uploadJobs.awaitAll()

        return@withContext uploadJobs.awaitAll()
    } catch (e: Exception) {
        throw FileUploadException("S3 File Upload fail")
    }
}

해당 코드는 이미지 파일 리스트를 파라미터로 받아서 AWS SDK를 통하여 S3 경로에 이미지를 업로드 처리를 하고 있습니다.

AWS SDK를 통하여 이미지에 대한 업로드 작업을 하는 코드이기 때문에 Dispatchers.IO로 사용하였고, Scope 내부에서 async를 사용하여 코루틴 job을 생성하고 있습니다. 그리고 awaitAll()을 사용하여 모든 코루틴 job이 처리될 때까지 대기합니다.

해당 코드에서는 모든 코루틴 job이 처리된 후 다음 프로세스가 수행되어야 하여 Coroutine Builder로 async를 사용하였고, 만약 각 작업의 완료 여부를 확인하거나 결과를 기다릴 필요가 없는 작업이라면 launch를 사용하시면 됩니다.



마치며

상세 기술서 이미지 개선 작업을 진행하면서 코루틴을 사용해 볼 수 있어서 좋은 경험이 되었던 것 같습니다. 부족한 글 읽어주셔서 감사드리고, 상품 상세 기술서 개선기 Part.2도 많은 관심 부탁드립니다!

상세기술서이미지코루틴
올리브영 테크 블로그 작성 상품 설명 영역 개선기 Part.1
⚡️
벙개맨 |
Back-end Engineer
벙개는 3일 전에 미리 말씀 부탁드립니다 🤙