Spring발행일 2024. 8. 10.원본 https://blog.naver.com/jword_/223543379315 ↗

자바 Optional 객체 사용이유?

자바 Optional 객체 사용이유? — #javaoptional #자바optional #Optional #옵셔널객체 #Optionalobject #개발자의도구들 🚨📝 📖 ?...

#Spring#Naver Blog

#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);

​

​