데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      SpringBoot로 MSA 구현하기(1) - Java에서 자주 쓰는 코드

      seungkyua 23.08.02
      5,418 17 0
      DEVOTEE 요약
      이 블로그는 Spring Boot를 사용하여 REST API 애플리케이션을 구축하기 위한 자주 사용되는 코드 패턴을 설명하고 있다. 주어진 코드는 null 체크, 기본값 설정, 예외 던지기, 배열과 리스트를 스트림으로 변환하기, 스트림을 다른 컬렉션으로 변환하기, 스트림과 데이터 변경 메소드 사용, enum 활용, 다양한 종류의 맵 사용 등을 다루고 있다. 이 코드들은 Spring Boot 개발자가 자주 사용하는 패턴들이며, Java로 개발하는 이유는 아직까지도 많이 사용되기 때문이다.
      DEVOTEE 추천 블로그

      MSA 패턴(CQRS, SAGA 등)을 코드로 구현하기 위해서 SpringBoot 으로 REST API 어플리케이션을 만드는 것을 정리하고 있는데 자료 구조외에 자주쓰게 되는 코드들이 있어서 이를 정리해 봤다.

      어플리케이션 개발에 Java 를 사용하는 이유는 아직 우리나라에서는 Java 가 많이 쓰이기 때문이다.

      SpringBoot 으로 어느 정도 정리되면 이후에는 Gin (Go 언어 기반 웹 프레임워크)으로 정리할 예정이다.


      1. null check

      Primitive Type 보다는 Object 를 많이 사용하기 때문에 null check 를 항상 해야 된다.

      String nullString = null;
      String emptyString = "";
      
      if (Objects.isNull(nullString))
          System.out.println("nullString is null");
      
      if (Objects.nonNull(emptyString))
          System.out.println("emptyString is not null");
      
      --------- output -----------
      nullString is null
      emptyString is not null

      Objects 클래스를 사용하여 가독성있게 null 체크를 할 수 있다.


      2. null check & default value 세팅

      null check 와 기본 값 세팅을 메소드 체이닝으로 바로 할 수 있다.

      BigDecimal bd = null;
      
      BigDecimal defaultValue = Optional.ofNullable(bd).orElse(BigDecimal.ZERO);
      System.out.println(defaultValue);
      
      --------- output -----------
      0

      Optional.ofNullable 은 static 메소드로 null 혹은 객체 를 담고있는 Optional 을 리턴한다.

      Optional.orElse 는 메소드로 객체의 값이 null 일 경우 넘겨주는 아큐먼트로 그 값을 채운다.

      여기서는 BigDecimal.ZERO 로 채워진 것을 알 수 있다.


      3. null check & throw Exception

      null check 와 null 일 때 예외를 던질 수 있다.

      BigDecimal bd = null;
      
      Optional.ofNullable(bd).orElseThrow(() -> new IllegalArgumentException("db is null"));
      System.out.println(defaultValue);
      
      --------- output -----------
      Exception in thread "main" java.lang.IllegalArgumentException: db is null
          at com.ask.example.Main.lambda$setDefaultValue$0(Main.java:45)
          at java.base/java.util.Optional.orElseThrow(Optional.java:403)
          at com.ask.example.Main.setDefaultValue(Main.java:45)
          at com.ask.example.Main.main(Main.java:24)

      orElseThrow 메소드는 function 을 파라미터로 받으며 여기서는 람다함수를 사용하여 간단히 IllegalArgumentException 을 생성하여 던진다.


      4. Array 와 List 를 Stream 으로 만들기

      Stream은 Java 8부터 추가된 기능으로, Collection(Array, List, Map 등)과 같은 데이터 요소들을 처리하는데 도움을 주는 기능이다. Stream을 이용하면 다양한 작업을 수행할 수 있다.

      1. 필터링(Filtering): filter() 메서드를 사용하여 특정 조건을 만족하는 요소들만 걸러낼 수 있다.

      2. 변환(Mapping): map() 메서드를 사용하여 요소들을 다른 형태로 변환할 수 있다.

      3. 소팅(Sorting): sorted() 메서드를 사용하여 요소들을 정렬할 수 있다.

      4. 그룹화(Grouping): collect() 메서드를 사용하여 요소들을 그룹화할 수 있다.

      5. 집계(Reduction): reduce() 메서드를 사용하여 요소들을 축소하여 하나의 값으로 만들 수 있다.

      6. 제한(Limiting): limit() 메서드를 사용하여 스트림의 크기를 제한할 수 있다.

      7. 건너뛰기(Skipping): skip() 메서드를 사용하여 스트림의 앞부분 요소들을 건너뛸 수 있다.

      8. 병렬 처리(Parallel Processing): parallelStream() 메서드를 사용하여 스트림 요소들을 병렬로 처리할 수 있다.

      9. 중복 제거(Distinct): distinct() 메서드를 사용하여 스트림의 중복 요소들을 제거할 수 있다.

      10. 조인(Joining): joining() 메서드를 사용하여 문자열 요소들을 합쳐서 하나의 문자열로 만들 수 있다.

      먼저 Stream 을 만들어 보자.

      배열을 Stream 으로 만들기

      String[] fruits = { "apple", "banana", "orange", "grape", "banana" };
      Stream stream = Arrays.stream(fruits);
      System.out.println(stream);
      
      --------- output -----------
      java.util.stream.ReferencePipeline$Head@36baf30c

      List 를 Stream 으로 만들기

      List<String> fruits = new ArrayList<String>(
              Arrays.asList("apple", "banana", "orange", "grape", "banana"));
      Stream stream = fruits.stream();
      System.out.println(stream);
      
      --------- output -----------
      java.util.stream.ReferencePipeline$Head@36baf30c


      5. Stream 을 다른 Collection 으로 바꾸기

      Stream 은 Collectors 클래스를 사용하여 다른 Collection 으로 쉽게 변경이 가능하다.

      Stream → Set 으로 변경 (중복 제거)

      참고로 distinct 로도 중복 제거가 가능하다.

      List<String> fruits = new ArrayList<String>(
              Arrays.asList("apple", "banana", "orange", "grape", "banana"));
      Set<String> newSet = fruits.stream()
              .collect(Collectors.toSet());
      newSet.forEach(item -> System.out.println(item));
      
      --------- output -----------
      banana
      orange
      apple
      grape

      중복된 banana 는 하나만 출력된다.

      Stream → LinkedList 로 변경

      List<String> fruits = new ArrayList<String>(
              Arrays.asList("apple", "banana", "orange", "grape", "banana"));
      List<String> newList = fruits.stream()
              .collect(Collectors.toCollection(
                      LinkedList::new
              ));
      newList.forEach(item -> System.out.println(item));
      
      --------- output -----------
      apple
      banana
      orange
      grape
      banana

      Stream → Map 으로 변경

      List<String> fruits = new ArrayList<String>(
              Arrays.asList("apple", "banana", "orange", "grape", "banana"));
      Map<String, Integer> newMap = fruits.stream()
              .collect(Collectors.toSet()).stream()
              .collect(Collectors.toMap(
                      Function.identity(),
                      String::length
              ));
      newMap.forEach((key, value) -> System.out.println(key + " " + value));
      
      --------- output -----------
      banana 6
      orange 6
      apple 5
      grape 5

      Map 은 중복 키를 가질 수 없기 때문에 Set 으로 변환한 다음에 Map 으로 변환하였다.

      여기서 Function.identity() 는 item 그 자체의 값으로 apple 과 같은 리스트의 요소를 가리키며, Map의 키로 사용되었다.

      String::Length 는 스트링 객체의 길이를 구하는 메소드를 호출한 리턴 값으로 Map 의 값으로 사용되었다.


      6. Stream 과 데이터 변경 메소드들

      Stream.map()

      map() 은 Item 각각에 대해서 다른 형태로 변경할 수 있다.

      List<String> fruits = new ArrayList<String>(
              Arrays.asList("apple", "banana", "orange", "grape", "banana"));
      List<String> newList = fruits.stream()
              .map(item -> item.toUpperCase())
              .collect(Collectors.toList());
      newList.forEach(item -> System.out.println(item));
      
      --------- output -----------
      APPLE
      BANANA
      ORANGE
      GRAPE
      BANANA

      map() 은 아규먼트로 function 을 받으며 결과로 Stream 을 반환한다. 아규먼트로 들어가는 함수는 각 요소를 변경하여 반환하는 함수이어야 한다.

      Stream.flatMap()

      flatMap() 은 중첩된 Collection 을 한꺼풀 벗긴다고 생각하면 된다.

      List<List<String>> listInList = Arrays.asList(
              Arrays.asList("apple", "banana"),
              Arrays.asList("orange", "grape", "banana"));
      
      List<String> newList = listInList.stream()
              .flatMap(Collection::stream)
              .distinct()
              .collect(Collectors.toList());
      newList.forEach(item -> System.out.println(item));
      
      --------- output -----------
      apple
      banana
      orange
      grape

      flatMap() 도 function 를 아규먼트로 받고 결과를 Stream 으로 변환한다. 단 아규먼트로 들어가는 함수는 각 요소에 영향을 주면서 Stream 을 반환하는 함수이어야 한다.


      7. enum 활용

      enum 은 enum 타입이 반환된다.

      enum Gender {
          MALE("male"),
          FEMALE("female");
      
          private final String param;
      
          Gender(String param) {
              this.param = param;
          }
      
          String getParam() {
              return this.param;
          }
      }
      
      System.out.println(Gender.MALE.getParam());
      
      --------- output -----------
      male

      Gender.MALE 값은 enum Gender 타입이다.


      하지만 아래와 같이 문자열을 enum 값으로 변환할 수 있다. 이 방법은 Json 을 marshal , unmarshal 할 때 많이 사용된다.

      enum Gender {
          MALE("male"),
          FEMALE("female");
      
          private final String param;
      
          private static final Map<String, Gender> paramMap =
                  Arrays.stream(Gender.values())
                          .collect(Collectors.toMap(
                                  Gender::getParam,
                                  Function.identity()
                          ));
      
          Gender(String param) {
              this.param = param;
          }
      
          static Gender fromParam(String param) {
              return Optional.ofNullable(param)
                      .map(paramMap::get)
                      .orElseThrow(() -> new IllegalArgumentException("param is not valid"));
          }
      
          String getParam() {
              return this.param;
          }
      }
      
      System.out.println(Gender.fromParam("male"));
      
      --------- output -----------
      MALE

      static 으로 Map<String, Gender> 변수를 선언하여 거기에 값을 채워넣고, static fromParam 메소드를 사용하여 String 값으로 Gender 값을 가져온다.


      8. Map 의 종류

      Map 에는 다음과 같은 종류가 있다.

      1. HashMap : key - value 로 저장되며 key 의 중복을 허용하지 않는다

      2. TreeMap : HashMap 의 기능에 key 가 정렬되어 있다.

      3. LinkedHashMap: HashMap 의 기능에 key 입력 순서대로 출력된다.

      4. MultiValueMap: HashMap 가 다른 점이 key 의 중복을 허용한다.

      다만, MultiValueMap 을 사용하기 위해서는 spring-core 패키지를 import 해야 한다.

      (apache commons 의 commons-collections4 에도 MultiValueMap 은 있지만 spring-core 가 더 사용하기 편하다)


      pom.xml 에 아래와 같은 dependcy 를 추가한다.

      <dependencies>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-core</artifactId>
              <version>6.0.9</version>
          </dependency>
      </dependencies>
      MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
      map.add("Accept-Encoding", "compress;q=0.5");
      map.add("Accept-Encoding", "gzip;q=1.0");
      
      map.forEach((key, value) -> System.out.println(key + " " + value));
      
      --------- output -----------
      Accept-Encoding [compress;q=0.5, gzip;q=1.0]

      REST API 를 만들 때 Header 에 값을 넣기 위해서 위와 같이 MultiValueMap 을 사용한다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      seungkyua 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기