1. 요구사항
- 편의점에서 동일한 품목이 여러 개씩 한번에 들어오는 상황이라고 가정
- 납품 업체에서는 품목의 정보를 담은 엑셀 파일을 보내주는 상황이라고 가정
- 품목의 정보에는 UUID(바코드 대신😅), 제품명, 가격, 유통기한의 정보가 들어있음
- 이 때, 제품명, 가격, 유통기한은 모두 같은 값을 갖지만 UUID는 모두 개별 값을 가짐
- 컴공 출신의 알바생은 이 상품들의 데이터를 다루는 코드를 짜보기로 함
2. Excel Template
UUID | name | price | Expiration Date |
60868a03-f082-40c7-8b67-d8ab9e3a9bfc | 삼각김밥 | 1200 | 2022. 1. 31 |
4bb35d56-b9a6-45b9-9741-723cdc9194df | |||
8fb9fca3-a26b-450a-92e5-22ec89e783f5 | |||
52e6aa00-063e-45ba-af23-6ce22ee037de | |||
8af3f578-3eaa-41a3-8213-e38a037a2b3b | |||
344a905b-01c3-4bc3-b02e-745896099ab4 | |||
0f84c25d-c404-4c74-a36f-0d4b6c5b2425 | |||
246ef737-7b57-4470-b194-55ea60bf1fae | |||
4cf4de9f-21e8-4377-a37e-a87701d5a1b1 | |||
4ddd04f2-aeb7-427b-b0df-6175eaec6474 |
기본적으로 무척 간단한 타입을 가진 데이터들인데, 날짜 타입만 엑셀의 날짜 서식 타입을 활용해서 저장해볼 것
3. 구현
build.gradle.kts
implementation("org.apache.poi:poi:4.1.1")
implementation("org.apache.poi:poi-ooxml:4.1.1")
Microsoft Office 파일을 Java 언어로 읽고 쓰는 기능을 제공해주는 Apache POI 라이브러리 의존성을 추가해준다.
(Kotlin을 활용해서 Spring 개발 환경을 구축하는 과정은 다음 Toy Project에서 더욱 자세하게 다뤄볼 예정!)
Item.kt
@Entity
class Item(
@Id val id: UUID,
@Column val name: String,
@Column val price: Int,
@Column val expiration_data: Date
)
실습 용도로 데이터를 간단하게만 담아볼 것이다.
ItemController.kt
@RestController
class ItemController(
private val itemService: ItemService
) {
@PostMapping("/item/bulk")
fun bulkCreate(
@RequestPart(name = "file", required = true) file: MultipartFile
) {
return itemService.bulkCreate(file)
}
}
POST /item/bulk 요청은 엑셀 파일을 받아서 벌크 연산을 할 수 있도록 해준다.
ItemService.kt
@Service
class ItemService(
private val itemRepository: ItemRepository
) {
fun bulkCreate(file: MultipartFile) {
val workbook = XSSFWorkbook(file.inputStream)
val worksheet = workbook.getSheetAt(0)
if (worksheet.physicalNumberOfRows <= 1) {
throw Exception("Uploaded excel file was invalid.")
}
val ids = mutableListOf<UUID>()
var name: String
var price: Int
var expiration_date: Date
for (rowIndex in 1 until worksheet.physicalNumberOfRows) {
try {
val row = worksheet.getRow(rowIndex)
if (rowIndex == 1) {
name = row.getCell(1).stringCellValue
price = Integer.parseInt(row.getCell(2).stringCellValue)
expiration_date = row.getCell(3).dateCellValue
}
ids.add(UUID.fromString(row.getCell(0).stringCellValue))
} catch (e: Throwable) { e.printStackTrace() }
}
for (id in ids) {
itemRepository.save(
id = id,
name = name,
price = price,
expiration_date = expiration_date
)
}
}
}
우선 Repository 부분은 기본적인 Spring Date JPA이기 때문에 생략했다. 😅
코드의 진행 과정은 다음과 같다.
- workbook 변수에 엑셀 파일 저장
- worksheet 변수에 해당 엑셀 파일의 0번 시트(첫번째 시트)를 저장
- 시트의 row 수가 1개 이하일 경우에는 예외 처리
- 0번 row는 분류 항목들을 담아두었기 때문에 1번 row부터 순회
- 1번 row일 경우에는 UUID 이외에도 name, price, expiration_date의 값들이 있기 때문에 별도로 처리하는 if문 로직 구현
- 1번을 포함한 모든 row에서 0번째 셀의 UUID 값을 Kotlin의 Mutable List를 활용하여 저장
- 저장한 id들을 순회하면서 모든 품목을 DB에 저장
4. 소감
새로 일하게 된 회사에서 부여받았던 첫 업무를 내 입맛대로 재구현해보았다.
사실은 직접 프로젝트를 생성하지도 않고 코드만 참조하면서 옮겨본 것이라 실제 환경에서 제대로 작동할지 미지수이지만,
적어도 업무에서는 이런 양상의 로직을 짜서 구현해내는 데에 성공했다.
그래도 혹시라도 따라하다가 누락된 부분 같은게 있다면 언제라도 부담없이 댓글 남겨주길 바란다! 🙇♂️
이번 좋은 기회에 처음으로 트러블슈팅 기록을 남겨보게 되었다.
문제가 너무 순탄히 해결되어서 과연 '트러블슈팅'이라고 부를 수 있을지 의문이긴 했지만,
그런 식으로 따지다간 끝도 없겠다는 생각이 들어서 그냥 업무 중 마주쳤던 크고 작은 문제들을 솔찬히 적어볼 생각이다.
그리고 부트캠프에서 다른 분들의 '개발을 시작하게 된 계기'를 듣다보면,
원래 사무직에서 엑셀 파일을 다루는 일을 많이 하던 분들이 이런 작업을 파이썬 언어를 통해 자동화를 해보다보니 개발에 흥미를 얻게 된 케이스가 정말 꽤 많았다.
'그런 작업들은 어떤 방식으로 하는 작업인가?' 하는 의문이 있었는데, 이번 기회에 그런 것들을 해소할 수 있었고, 이와 더불어서 내 개발 역량의 폼도 조금이나마 늘지 않았나 싶다.