자바 Optional 객체 사용이유?
자바 Optional 객체 사용이유? — #javaoptional #자바optional #Optional #옵셔널객체 #Optionalobject #개발자의도구들 🚨📝 📖 ?...
#javaoptional #자바optional #Optional #옵셔널객체 #Optionalobject #개발자의도구들
🚨📝 📖 📒
AI스쿨 msa기반 java 백엔드 코스 중에 공부한 내용을 작성하였습니다
\* 현재 Spring 프로젝트를 진행 중입니다. 전체적인 목차를 보시려면 여기를 눌러주세요.
문제발생
| public static String getText(String key) { String sql = "SELECT text\_value FROM texts WHERE text\_id = (?)"; try { PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, key); ResultSet resultSet = preparedStatement.executeQuery(); preparedStatement.close(); if (resultSet.next()) { return resultSet.getString("text\_value"); } resultSet.close(); } catch (SQLException e) { System.out.println("DataBase Manger: getText err: " + e.getMessage()); } return null;} |
|---|
DataBase로직을 수행하고 제대로 동작이 완료했는지 알리기 위해 String 객체와 null객체를 전달하였습니다. 하지만 이 방법은 굉장히 고전적인 방법으로, 요즘에는 좀 더 효율적으로 상태 전달이 가능한 Optional을 사용한다고 하는데요. 오늘은 Optional 객체가 무엇이며, 어떻게 사용하면 좋을지 함께 공부하는 시간이 되었으면 좋겠습니다.
상태를 표현하는 방법
Boolean
Boolean은 true & false로 두 가지의 "상태"만을 표현할 수 있습니다. 위 상황에서 만약 boolean으로 return한다면 어떤 결과가 나타날까요?
| public static boolean getText(String key) { String sql = "SELECT text\_value FROM texts WHERE text\_id = (?)"; try { PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, key); ResultSet resultSet = preparedStatement.executeQuery(); preparedStatement.close(); if (resultSet.next()) { return true; } resultSet.close(); } catch (SQLException e) { System.out.println("DataBase Manger: getText err: " + e.getMessage()); } return false;} |
|---|
위 상황에서는 boolean이 매우적절하지 않습니다. 이유는 getText의 method역할의 본질 때문이기도 합니다. 현재 로직은 DB에서 원하는 데이터를 가져오는 과정인데, 여기서 boolean을 던진다면, 단순 데이터를 가져오는데 성공했는지 실패했는지 "상태"만 확인이 가능하기 때문입니다.
| 💡 boolean은 상태만 표시가 가능하다. 값을 전달 할 수 없다. |
|---|
null
| public static String getText(String key) { String sql = "SELECT text\_value FROM texts WHERE text\_id = (?)"; try { PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, key); ResultSet resultSet = preparedStatement.executeQuery(); preparedStatement.close(); if (resultSet.next()) { return resultSet.getString("text\_value"); } resultSet.close(); } catch (SQLException e) { System.out.println("DataBase Manger: getText err: " + e.getMessage()); } return null;} |
|---|
문제가 발생한 위 코드는 DB에서 로직을 성공했을 경우 String으로 데이터를 반환합니다. 하지만, 만약 DB로직이 실패한다면 null을 반환하도록 되어있습니다.
이는 표면상에는 큰 문제가 없어 보입니다. 원하는 key값으로 데이터를 찾지 못할 수 있다는 것을 대부분의 경우에 암묵적으로 알고 있기 때문입니다.
하지만, 이 코드말고도 좀 더 복잡한 환경에서 생각해 봐야 합니다. 한 프로젝트 내에 수백가지의 method가 있고, 해당 method마다 null이 발생할지 안할지 여부를 감각적으로 100% 알기가 어렵습니다. 그럼 결국에 일일이 null값을 반환하는지 체크를 해야합니다. 왜냐하면 호출한 곳에서 null을 제대로 처리가 안된 경우 nullPointerException이 발생하여 서비스 자체가 다운되는 현상이 생길 수 있기 때문이지요.
또한 개발자가 적절하게 NULL을 처리하지 못할 수도 있습니다.
| 💡null을 직접적으로 반환하는 것은 문제가 없어 보인다. 하지만, 대규모 프로젝트에서 일일이 모든 method의 null 반환을 체크하는 것은 비효율적이다. |
|---|
Optional
이에 대한 대응으로 Optional 객체를 사용할 수 있습니다. Optional은 객체 내부에 반한 값을 넣을 수 있으며, null 값 표시도 가능합니다.
즉 호출자는 method에 Optional이 붙어있는 경우 해당 method가 null을 반환한다는 것을 직관적으로 알 수 가 있습니다.
public static Optional<String> getText(String key) { String sql = "SELECT text\_value FROM texts WHERE text\_id = (?)"; try { PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, key); ResultSet resultSet = preparedStatement.executeQuery(); preparedStatement.close(); if (resultSet.next()) { return Optional.of(resultSet.getString("text\_value")); } resultSet.close(); } catch (SQLException e) { System.out.println("DataBase Manger: getText err: " + e.getMessage()); } return Optional.empty;} |
|---|
위는 Optional.of()를 사용하여 String을 넣어 반환하거나, Optional.empty()를 사용하여 null값을 전달하고 있습니다.
호출자는 해당 메서드가 반환값이 Optional 객체임을 알고있기에 자연스럽게 null을 적절하게 처리할 것 입니다. 또한 Optional 자체의 내장 method들을 사용하여 실수 없이 null값을 처리할 수 있습니다.
Optional 관련 method 정리
생성 메서드
Optional.of(T value)
Optional객체에 반환 자료형을 넣어 객체를 생성하는 메서드입니다.
Optional<String> optional = Optional.of("Hello"); |
|---|
Optional.empty()
빈 Optional 객체를 만듭니다.
Optional<String> optional = Optional.empty(); |
|---|
Optional.ofNullabe(T value)
value가 null일 수도 있는 경우의 Optioanl객체를 만듭니다.
Optional<String> optional = Optional.ofNullable(name); // name이 null이라면-> Optional.empty()가 반환되는 것 |
|---|
확인 메서드
Optional.isPresent()
Optional의 값이 존재 하면 true, 존재 안하면 false를 반환합니다.
| if (optional.isPresent()) { // 값이 존재할 때 처리} |
|---|
Optional.isEmpty()
Optional의 값이 존재 하면 false, 존재 안하면 true를 반환합니다.
| if (optional.isEmpty()) {// 값이 존재하지 않을 때 처리} |
|---|
반환 메서드
Optional.get()
Optional에 존재하는 자료형을 가져옵니다.
| String value = optional.get(); |
|---|
Optional.orElse(T other)
Optional에 값이 존재하면 반환하고, 그렇지 않으면 대체값을 반환합니다.
| String value = optional.orElse("Default Value"); |
|---|
Optional.orElse(Supplier<? extends T> supplier)
Optional에 값이 존재하면 반환하고, 그렇지 않으면 대체값 공급하는 Supplier의 결과를 반환합니다.
| String value = optional.orElseGet(() -> "Default Value"); |
|---|
Optional.orElseThrow(Supplier<? extends X>
Optional에 값이 존재하면 반환하고, 그렇지 않으면 주어진 예외를 발생시킵니다.
| String value = optional.orElseThrow(() -> new IllegalArgumentException("No value present")); |
|---|
값 변환 메서
Optional.map(Function<? super T, ? extends U> mapper)
Optional에 값이 존재하면 주어진 함수로 변환후 그 변환 값이 들어있는 Optional을 반환합니다. 존재하지 않는 경우 Optional을 반환합니다.
Optional<Integer> length = optional.map(String::length); |
|---|
