mj@home:~$

UTF8인코딩 csv 파일을 excel에서 열기

그나마 최근 버전의 excel(아마.. 2013+)에서는 아무런 조치없이도 utf-8인코딩을 지원하지만 그 이전 버전은 텍스트로 따로 열어야 하던가 다른 text editor를 가지고 인코딩을 euc-kr이나 ansi로 변경해서 열어줘야했다. 그러나 이전 버전에서도 utf-8인코딩인 csv파일을 열수 있다. utf-8 앞의 BOM(byte order mark)을 달아주는 것이다.

BOM은 UTF종류 앞에 붙는 바이트로 UTF-8, UTF-16 등등으로 읽을 수 있게 지정해주는 것이다.

변경하는 방식은 간단하다. 서번에서 csv파일 데이터를 byte []로 만들어 client로 보낼 때, 바로 앞에 [239,187,191]배열을 추가해주면된다. 한번 추가로 안될 경우 [239,187,191,239,187,191] 이렇게 두번을 넣어서 보내주면된다. 서버에서 전송하는 encoding은 UTF-8로 지정해주면 된다.

javascript에서 csv파일로 만들어 저장하면 인코딩 형식이 UTF-8 BOM으로 열리는 것을 확인 할 수 있다.

필자가 사용하는 CSV변환 유틸을 첨부한다.

public class CsvUtil {

	/**
	 * 목록을 받아 CSV 데이터 형식으로 바꿔주는 함수
	 *
	 * @param list
	 * 			데이터 목록
	 * @param clazz
	 * 			데이터 타입
	 * @return CSV 데이터
	 */
	public static byte [] createCsv(List<?> list, Class<?> clazz) {
		String csvString = "";
		try {
			Field [] fieldList = clazz.getDeclaredFields();
			List<String> fieldName = Arrays.asList(fieldList).stream()
																.map(field -> (field.getName()))
																.collect(Collectors.toList());

			csvString = createHeader(fieldName);

			for (Object item : list) {
				csvString = csvString + CSVWriter.RFC4180_LINE_END + creatCsvLine(item, fieldList, clazz);
			}
		} catch (IllegalArgumentException iae) {
			iae.printStackTrace();
		} catch (IllegalAccessException iae) {
			iae.printStackTrace();
		} catch (InstantiationException iae) {
			iae.printStackTrace();
		}

		return ArrayUtils.addAll(encodingUTF8BOM(), csvString.getBytes());
	}

	/**
	 * CSV 헤더를 만들어주는 함수
	 *
	 * @param fileNameList
	 * 			필드 이름 목록
	 * @return CSV 헤더
	 */
	private static String createHeader(List<String> fileNameList) {
		String header = "";

		for (String fileName : fileNameList) {
			if (StringUtils.equals(header, "") == true) {
				header = fileName;
			} else {
				header = header + CSVWriter.DEFAULT_SEPARATOR + fileName;
			}
		}

		return header;
	}

	/**
	 * 한 줄의 CSV 데이터를 만들어 주기 위한 함수
	 *
	 * @param obj
	 * 			데이터
	 * @param fieldList
	 * 			데이터가 갖고 있는 필드 목록
	 * @param clazz
	 * 			데이터 타입
	 * @return CSV 데이터
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	private static String creatCsvLine(Object obj, Field [] fieldList, Class<?> clazz) throws IllegalArgumentException, IllegalAccessException, InstantiationException {
		String line = "";

		if (StringUtils.equals(obj.getClass().getName(), clazz.getName()) == true) {
			for (int i = 0 ; i < fieldList.length ; i++) {
				boolean accessible = fieldList[i].isAccessible();
				fieldList[i].setAccessible(true);

				if (StringUtils.equals(line, "") == true) {
					line = (String)fieldList[i].get(obj);
				} else {
					line = line + CSVWriter.DEFAULT_SEPARATOR + fieldList[i].get(obj);
				}

				fieldList[i].setAccessible(accessible);
			}
		}

		return line;
	}

	/**
	 * csv가 excel에서도 정상적으로 열리게 하기 위한 인코딩 추가 함수
	 *
	 * @return
	 */
	private static byte [] encodingUTF8BOM() {
		byte [] encoding = new byte[6];

		encoding[0] = (byte) 239;
		encoding[1] = (byte) 187;
		encoding[2] = (byte) 191;
		encoding[3] = (byte) 239;
		encoding[4] = (byte) 187;
		encoding[5] = (byte) 191;

		return encoding;
	}

}