23년 학생회 사무국장을 맡게 되었다.
학생회비 납부명단을 excel로 넘겨받았더니 형식도 학번 마다 다르고 복지 사업할 때 납부 여부 확인이 굉장히 귀찮아보였다.
(실제로 복지 사업에서 납부명단을 확인하는건 꽤나 귀찮은 작업이었다. 학번 별로 ctrl+f를 수십번해야되는...)
이참에 프로그램을 하나 만들어 손쉽게 납부여부를 확인할 수 있게 만들어보자 생각이 들었고 만든 과정을 글로 써보기로 했다.
Java에서 excel의 xlsx파일을 읽어오려면 poi 라이브러리를 사용해야한다.
poi는 자바의 기본 내장 라이브러리가 아니므로 poi 사이트에서 다운을 해서 인테리제이 IDE의
[File] > [Project Structure] > [Libraries] > [+]
로 이동하여 추가해주면 된다.
하지만 이렇게 되면 나 말고 다른 사람이 쓰는 환경에서는 라이브러리가 깔려있지 않기 때문에 새로운 컴퓨터로 프로그램을 옮기면 다시 라이브러리 세팅을 해야된다는 문제가 생겼다.
첫번째 문제 - local 라이브러리 jar파일을 어떻게 사용자별 세팅없이 사용할 수 있을까?
고민하다가 생각난건 spring project를 만들다보면 gradle로 여러 라이브러리를 편하게 프로젝트에 종속시켜 사용했었다.
그래서 gradle프로젝트로 바꾸기로 생각을 했고 하다보니 여러 error에 부딪히며 그냥 spring-boot 프로젝트로 만들기로 했다..
찾아보니 poi 라이브러리가 gradle에 있었고
implementation group: 'org.apache.poi', name: 'poi-ooxml', version: '4.1.2'
implementation group: 'org.apache.poi', name: 'poi-ooxml-schemas', version: '4.1.2'
implementation group: 'org.apache.poi', name: 'poi', version: '4.1.2'
implementation group: 'org.apache.commons', name: 'commons-collections4', version: '4.4'
implementation group: 'org.apache.xmlbeans', name: 'xmlbeans', version: '3.1.0'
dependencies에 다음과 같이 추가해주었다.
poi라는 영어가 붙은 라이브러리들은 꼭 같은 버전으로 맞춰주어야한다. (필자는 4.1.2 버전을 사용하였다.)
버전을 맞춰주지 않으면 버전 차이로 인해 특정 함수들 사용이 안될 수 있으니 참고하자.
(특히 4. 대 버전과 3. 대 버전은 cell의 row를 읽어올 때 타입 구분하는 방식이 다르니 참고)
위와 같이 설정후 프로그램을 docker image로 만들어 실행해보니 또 다른 문제가 발생하였다.
두번째 문제 - docker는 cli 처럼 사용자 입력을 받아올 수 없다..
Docker에 배포를 해서 나눠주자. docker image로 만들어두면 terminal 창에서 다운받아 잠깐의 안내로 학생회 국원들도 쓸 수 있을 것 같다는 행복한 생각은 잠시..
사용자 입력이 안받아진다는 것을 알고 이번에는 exe 프로그램으로 만들어야겠다고 생각했다.
하지만 mac에서는 exe 프로그램 실행이 어려우므로 그냥 jar 파일로 만들고 jar 파일로 나눠줘야겠다고 생각했다..
jar 파일은 gradle 프로젝트라면
./gradlew build
를 해주면 [build] > [libs] 에 jar 파일이 생긴다.
jar 파일은 해당 디렉토리에서
java -jar *.jar
를 하면 실행된다. (jar 파일을 디렉토리에 한개만 두자.. 2개 이상시 이름을 명확히 표기해주어야한다.)
다음과 같이 학생회국원분들께 나누어주었다.
현재는 윈도우 사용자 국원들을 위해 exe 프로그램으로 나누어주고 mac 사용자들은 sh 프로그램으로 클릭하면 터미널에서 실행되게 배포하였다.
코드 설명
핵심 코드인 excel 파일 읽는 부분만 가져와봤다.
private static boolean readExcel(int studentYear,String name) {
boolean isContain = false;
try{
String fileName = "컴퓨터공학부 "+ studentYear +" 학생회비 납부여부.xlsx";
FileInputStream file = new FileInputStream(fileName);
XSSFWorkbook workbook = new XSSFWorkbook(file);
int rowIndex =0;
//시트수
//시트가 여러개면 for문으로 처리해줘야함
XSSFSheet sheet = workbook.getSheetAt(0);
//행의 수
int rowNum = sheet.getPhysicalNumberOfRows();
for (rowIndex = 0; rowIndex < rowNum; rowIndex++) {
boolean tmp = readCell(rowIndex, sheet,name);
if(!isContain)
isContain = tmp;
}
}
catch (IOException e)
{
System.out.println("파일이 존재하지 않습니다.");
}
return isContain;
}
readExcel() 함수는 학번 년도랑 이름을 받아온다.
해당하는 학번을 이름으로 가지는 파일을 XSSWorkbook 오브젝트로 만들어준다.
필자의 excel 파일은 파일당 시트가 하나였으므로 0번째 sheet만 가져왔다.
이제 readCell() 함수를 각 행의 개수만큼 진행주자.
readCell()은 각 row의 데이터들을 읽어오는 함수이다
private static boolean readCell(int rowIndex, XSSFSheet sheet, String name) {
int columnIndex;
XSSFRow curRow = sheet.getRow(rowIndex);
if(curRow != null)
{
int cellNum = curRow.getPhysicalNumberOfCells();
if(cellNum <6 )
return false;
String[] cellValues = new String[cellNum];
for (columnIndex = 0; columnIndex <cellNum ; columnIndex++)
{
//셀 값 읽기
XSSFCell curCell = curRow.getCell(columnIndex);
if(curCell != null)
cellValues[columnIndex] = readCellByType(curCell);
}
String num = cellValues[0];
String cellStudentID = cellValues[1];
String cellName = cellValues[2];
String phoneNumber = cellValues[3];
String cellPaymentCheck = cellValues[4];
String department = cellValues[5];
if(cellPaymentCheck.equals("o")||cellPaymentCheck.equals("O"))
count++;
if(cellName.equals(name))
{
if(cellPaymentCheck.equals("false"))
cellPaymentCheck = "x";
if(phoneNumber.equals("x"))
phoneNumber ="전화번호 미등록";
if(cellStudentID.equals("x"))
cellStudentID ="학번 미등록";
System.out.println("---------------------------------");
System.out.println(name+" 학생의 학번 : "+ cellStudentID);
System.out.println(name+" 학생의 학과 : "+ department);
if(phoneNumber.equals("전화번호 미등록"))
System.out.println(phoneNumber);
else
System.out.println(name+" 학생의 전화번호 : 0"+ phoneNumber);
System.out.println(name+" 학생의 학생회비 납부 여부 : "+ cellPaymentCheck);
System.out.println("---------------------------------");
return true;
}
}
return false;
}
XSSFSheet 오브젝트에서 XSSFRow를 가져와서 각 XSSFCell의 데이터를 읽어서 String 값으로 변환해주고 출력해주자.
private static String readCellByType(XSSFCell curCell) {
String value ="";
switch (curCell.getCellType())
{
case FORMULA:
value= curCell.getCellFormula();
break;
case NUMERIC:
int numericCellValue = (int)curCell.getNumericCellValue();
value = String.valueOf(numericCellValue);
break;
case STRING:
value= curCell.getStringCellValue()+"";
break;
case BLANK:
value= curCell.getBooleanCellValue()+"";
break;
case ERROR:
value= curCell.getErrorCellValue()+"";
break;
}
return value;
}
각 셀의 data Type 별로 String 변환 처리가 다르므로 switch문으로 처리해주었다.
poi 라이브러리 버전에 따라 방법이 살짝식 달라지므로 유의하자
후기
excel 파일 조회 프로그램을 만드는 것 자체는 굉장히 쉬웠지만 다른 사람들이 이걸 쉽게 사용하게 하는건 다른 문제인 것을 알았다.
앞으로도 개발을 할때 사용자 관점에서 다시 한번 생각해볼 수 있는 경험을 얻었던 것 같다.
지금 생각한건 jar를 파일을 배포하는 것이 최선이었지만 혹시 사용이 더 편한 다른 방법이 있다면 댓글로 알려주었으면 좋겠습니다.