728x90

이진 트리 순회

불과 반년전만 해도 이름만 들었지 마냥 먼곳에 있다고 생각했던 자료구조들이다.
근데 공부하면서 깨닫는 것은 뭐를 알아야 준비를 하고 공부도 하고
재밌게 문제도 풀 수 있다는 것이다. 그것이 바로 코딩테스트 😱
DFS니 BFS니 하려면
일단 스택, 큐, 배열, 재귀에 대해서 알아야된다고 생각했다.
물론 그리고 지금 포스팅하는 이 이진 트리에 대해서도 좀 짚고 넘어가야 한다고 봤다.

이진트리란?

이진트리는 각각의 노드가 아래 자식 노드를 최대 두개를 가진 트리 자료 구조이다.

이진트리 예시

위 이미지는 위키백과 에서 가져와봤다.
깊이(depth)는 3이고 크기는 9인 이진트리이다.

     1
  2     3
4  5   6  7

이런식으로 구성된 트리가 있을 때 전위 표기식으로 순서를 나타내는 알고리즘을 구성해보자

코드

public class Main {
    private static class Node {
        private int value;

        private Node left;
        private Node right;

        public Node(int value) {
            this.value = value;
            left = right = null;
        }
    }

    private static void dfs(Node n) {
        if (n == null) {
            return;
        }

        System.out.print(n.value + " ");
        dfs(n.left);
        dfs(n.right);
    }

}

자기 자신의 노드 그리고 left, right의 자식 노드를 알고있어서 재귀로 다음 노드를 호출하며

null인 경우에는 바로 return을 해주어 바로 다음 로직으로 이동하게끔 구현이 되었다.

전위 순회로 출력하게 되면 1 2 4 5 3 6 7 순서로 나오게 된다.

728x90

'CS > 자료구조' 카테고리의 다른 글

자료구조  (0) 2022.08.10
728x90

@ExceptionHandler

예외 처리기가 어떻게 동작하는지에 대해서 궁금했어서
업무중에 돌려보게 되었다. (예제코드는 다시 작성할 예정)
일단 동작과정은 DB에서 해당 id를 찾아 검색했을 때 없을 경우 예외를 던져주게 하는

예시

간단하게 보면

public class UserService {
    private final UserRepository userRepository;

    public User findById(final int id) {
        return userRepository.findById(id)
                             .orElseThrow(() -> new NotFoundException("해당 유저를 찾을 수 없습니다"));
    }
}

라고 로직을 구성했을 때 이 로직의 예외에 대한 핸들러 동작을 파보게 되었다.
일단 get 메소드로 조회 로직을 수행하고

그 요청을 RequestMappingHandlerAdapter로 위임해서 처리를 요청한다.

그다음 주어진 값들로 컨트롤러에 대한 로직을 처리하는데

InvocableHandlerMethod

다음 그림이 InvocableHandlerMethod 클래스이다.

스크린샷 2022-02-09 오전 11 14 03

이 때, getBridgedMethod().invoke(getBean(), args); 부분이

cglibAopProxy쪽으로 조회 로직을 맡기게 되고 그 곳에서 repository에 대한 동작을 수행하다가
findById에서 못찾았을 경우에 에러를 던지게 된다.
다시 cglibAopProxy에서 요청결과에 대한 에러를 잡아서 다시 처리되는데

이것이 InvocableHandlerMethodcatch로 넘어온다.

catch (InvocationTargetException ex) 이부분에서

RuntimeException의 인자인지를 확인한다.

NotFoundException

이 부분에서 내가 구현한 NotFoundException 의 상속도는

NotFoundException -> NoSuchElementException -> RuntimeException -> Exception 순서기 때문에

instanceof RuntimeException 으로 처리가 되게 된다.

그렇게해서 exception 객체를 담아서 DispatcherServlet 으로 넘겨주게 된다.

DispatcherServlet

이젠 디스패쳐 서블릿이 받은 에러를 처리해줄 누군가를 찾기 시작하는데

스크린샷 2022-02-09 오전 11 18 55

HandlerExceptionResolverComposite

스크린샷 2022-02-09 오전 11 19 56

이 두 개중 HandlerExceptionResolverComposite 로 처리를 진행해준다.

그렇게 해서 resolveException 메소드를 실행해주는데

다음 이미지를 보게되면 resolvers에 3개가 들어있게 된다.

스크린샷 2022-02-09 오전 11 20 44

그 리졸버들이 바로 HandlerExceptionResolver 들을 구현한것들

스크린샷 2022-02-09 오전 11 22 39

그중에서도 나는 ResponseStatusException을 날려준게 아니기 때문에

그중에서 최종적으로 HandlerExceptionResolver 인터페이스 구현체인

ExceptionHandlerExceptionResolver, ResponseStatusExceptionResolver, DefaultHandlerExceptionResolver

세개를 가지고 for문을 돌게 된다

InvocableTargetException 이라는 객체의 target

즉, 대상이 어떤 Exception이냐에 따라서 Exception 에맞는 @ExceptionHandler로 파싱되어 커스텀된 응답으로 나가게 된다.

마무리

똑같은 로직 이라고 가정했을때 미묘하게 최적화를 하고싶다? 한다면

HandlerExceptionResolver 리스트의 맨앞인 ExceptionHandlerExceptionResolver를 사용 그러니까

커스텀으로 @ExceptionHandler 를 만들어 쓰는것이 ResponseStatusException 을 던지거나, @ResponseStatus 을 사용하는것보다

빠를 수 있겠다.

728x90

'Spring' 카테고리의 다른 글

@Valid, @Validated 차이  (0) 2022.08.10
AOP  (0) 2022.08.10
Spring Rest Docs 테스트로 문서화를 해보자!  (0) 2022.08.10
Service Layer에 대한 생각  (0) 2022.08.10
728x90

선택 정렬 (Select Sort)

선택 정렬은 현재 위치에 들어갈 데이터를 찾아 선택하는 알고리즘이다.
오름차순을 기준으로 정렬한다.

개념

제자리 정렬의 알고리즘 중 하나이다.
정렬 되지 않은 입력된 배열 외에 다른 메모리를 사용하지 않는다.
해당하는 n번째에 넣을 정렬된 원소 자리는 이미 정해져있고,
어떤 값을 넣을지를 선택하는 알고리즘이다.

동작 과정

  1. 주어진 배열에서 최솟값을 찾는다.
  2. 그 최솟값을 배열의 맨 앞의 수와 자리를 교체해준다.
  3. 맨 처음 값을 뺀 나머지 배열로부터 최솟값을 찾는다.
  4. 교체한 다음 맨 앞의 배열과 값을 바꿔준다.
  5. 이 과정을 정렬이 완료될 때까지 계속 반복한다.

스크린샷 2022-02-07 오후 10 19 41

보기 좋은 예시 이미지를 가져와봤다.
이제 그러면 구현을 해보도록 하자.

Select Sort 구현

n 길이를 가진 배열을 생성하고 다음줄에 n개의 숫자를 입력받아 선택정렬 한다.

public class SelectionSort {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine()); //길이가 n
        StringTokenizer st = new StringTokenizer(br.readLine());

        int[] arr = new int[n]; //n개의 숫자를 넣을 배열

        for (int i = 0; i < n; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        final int[] results = solution(arr);

        for (int result : results) {
            System.out.print(result);
        }
    }

    private static int[] solution(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }

            //최소값과 해당 반복 index의 맨 앞자리와 치환
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
        return arr;
    }
}

결론

이런 사소한 지식이라도 안적어두면 까먹고 또 기억이 안나는것 같다.
위의 예시도 ide도움 없이 손코딩으로 포스팅을 올려보는데 더 상기되서 까먹지 않을것 같다.
계속 까먹기 때문에 이렇게 기록으로 남겨두는것이 좋을듯 하다. 😎

728x90

'CS > 알고리즘' 카테고리의 다른 글

에라토스테네스의 체  (0) 2022.08.09
728x90

자료구조

자료 구조의 사전적인 의미는,
효율적인 접근 및 수정을 가능케하는 자료의 조직, 관리, 저장을 의미한다.
그래서 이 자료구조는,
데이터들의 값 모임, 데이터간 관계 들을 의미한다.
이 사전적 의미를 보니까 알고리즘과 뗄래야 뗄 수가 없는거 같다.
결국 이 선택에 따라 효율적으로 알고리즘을 설계를 할 수 있으니까 말이다.

들어가며

이 자료구조들은 당연히 여러 가지가 있으며, 각각의 자료구조는 각자의 연산과 목적에 맞춰져 있다.

단순히 알고리즘 문제들만 풀 때 자료구조를 선택한다? 답은 NO

어떠한 기능을 설계함에 있어 자료구조를 선택하는 것은 필수일 것이다.
자꾸 예를 알고리즘으로 들어서 그렇지만, 이만큼 알고리즘과 뗄래야 뗄 수가 없다는것.
자료구조가 명확해지면, 그에 따라오는 알고리즘이 반드시 필요한게 있을 거라고 본다.

종류

자료의 특성, 크기, 사용법, 연산에 따라 여러가지 종류가 있다.
크게 단순 구조, 선형 구조, 비선형 구조, 파일 구조로 나눌 수 있다.

스크린샷 2022-02-01 오후 9 42 40

단순 구조

  • True/False, 정수, 실수, 문자 및 문자열과 같이 컴퓨터가 기본적으로 제공하는 자료형

선형 구조

  • Array(배열)
    • 가장 일반적인 구조
    • 메모리 상에 같은 타입의 자료가 연속적으로 저장
    • 자료값을 나타내는 가장 작은 단위
  • LinkedList
    • 노드를 하나의 단위로 한다.
    • 노드는 자료와 다음 노드를 가리키는 참조값으로 구성
    • 점 조직 같은 느낌
  • Stack
    • 후입선출(Last-In-First-Out)
    • 먼저 저장된 것이 마지막에 나오게 되는구조
    • 자료의 나열 순서를 바꾸고 싶다면 스택에 넣었다가 꺼내면 역순으로 변경된다.
  • Queue
    • 선입선출(First-In-First-Out)
    • 먼저 저장된 것이 먼저 나오게 되는 구조
  • Deque
    • 양쪽에서 넣기 빼기를 할 수 있는 구조

비 선형 구조

  • Graph
    • 꼭짓점과 꼭짓점을 잇는 변으로 구성된다.
  • Tree
    • 뿌리와 뿌리 또는 다른 꼭짓점을 하나의 부모로 갖는 꼭짓점들로 이루어진 구조
    • 부모와 자식의 관계는 변으로 표현

파일 구조

하드디스크 같은 보조 기억장치에 저장되는 파일에 대한 자료구조
메모리에 한번에 로드할 수 없는 대용량의 자료
파일 구성 방식에 따라 순차, 색인, 직접으로 나뉘게 된다.

728x90

'CS > 자료구조' 카테고리의 다른 글

이진트리  (0) 2022.08.10
728x90

728x90

'Spring' 카테고리의 다른 글

AOP  (0) 2022.08.10
@ExceptionHandler  (0) 2022.08.10
Service Layer에 대한 생각  (0) 2022.08.10
Validaion  (0) 2022.08.09
728x90

회고록

블로그를 옮기게 되어 날짜가 맞지 않는다.

작성일 : 2022년 2월 21일

 

서비스 회사로 와서 벌써 1달 반정도가 지났다.
스타트업인지라, 먼저 있었던 사람들을 욕할건 아니다.
그렇지만 지금 그렇게 쳐나가면서 생겼던 기술부채로 인해서 신규 개발건이 들어왔을 때
유지보수가 힘든 점들이 많다.

무책임했던 누군가

기존에 있던 백엔드 개발자가 나가고 없었기에 모르겠지만 그사람은

자바에 엄청난 자부심이 있다고 했다.

<<-- 이렇게 하면 안되지만 정말 못짰다..🤬

협업이라는 것을 싫어했고, 자기의 의견이 맞았으며, 코딩 컨벤션 또한 지켜진게 없었다.
그리고 외주로 뭔가 개발이 왔던건줄 알았던 코드가 그 사람의 코드였다...

악취나는 코드의 예시들

클래스가 MAIN_CLASS, main_class, MainClass, mainClass 등등... 네이밍이 상당했다.

이걸 나의 사수분께서는 JDBC -> JPA로 리팩토링을 하고 계셨던 찰나에 내가 입사를 했던 것이다.
많이 봐서 익숙했던 HTTP 상태 코드도 쓰지 않고 커스텀으로 200을 정의했는데 이게 에러였나? 그랬다.

switch(STATUS_CODE) {
    case 200: 
    case 201: 
    case 202: 
    case 203: 
    case 204: 
    // 이렇게 엄청 많이 있었다
}

이렇게 아마 300번까지 있나 그런다.. 너무 보기싫다.. 😇

아니 ResponseBody를 전역에서 설정해준것이 바로 @RestController가 아닌가?

난 내 지식이 의심스러울 정도였다.
간략하게나마 써보자면...
예를들어 유저를 조회하려는 API가 있다고 하면..

 

나름의 컨벤션이 있던건 같다. 클래스 뒤에 1은 create, 2는 read, 3은 delete, 4는 update였다 ㅋㅋㅋㅋ

 

아직도 웃기다.. 🤣🤣🤣🤣
아 아래 예시는 조회인데 조회 API 클래스만 따로있다.
그러니까 위에서 했던 1, 2, 3, 4 일련의 동작들이 하나로 엮인게 아니라
1기능 당 1개의 컨트롤러다.

@RestController
public class h_user_info_2 {

    @ResponseBody
    @RequestMapping(value = "/pood/user/view/2", method = RequestMethod.POST)
    public String USER_INFO_LIST_VIEW(@RequestBody h_user_info_vo2 header) { // 이런식으로 받아온다.. + 조회인데 POST + api 뒤에 숫자도 붙는다..
        // 왜 실행하고 있는지 모르겠는 로직...
        // 뭐 대략 이런식으로 돌아간다
        return response;
    }
} 

아무튼 뭐 이런식으로 구성이 되어있었으니 상당히 머리아프고 파악도 못해먹겠다.
그래서 사수도 엄청 답답했을 것이다.
이걸 JPA로 바꾼 사수님은 👍👍👍
하지만 그래도 이거로는 부족했다.

객체지향적으로 코드를 구성해보자

일단 바꾼것, 가독성을 높인건 사실 맞다.

하지만, 여기서도 문제가 있었는데 흔히 보는 JPA의 Repository인 UserRepository (예시)

이 Repository를 어떤 서비스에서 필요로 할 때마다 의존 주입을 해서 쓰고있던것이다.

여기서 이제 이것도 나는 바꿔야 겠다고 마음을 먹었고, 하나의 서비스 -> 하나의 리파지토리 를 의존하는 것이 가장 좋다.

라고 생각을 했다.

그리고 여러 비즈니스 로직을 담는 클래스 -> 서비스 인것 같은 뉘앙스가 많이 풍겼다.

스크린샷 2022-01-26 오후 9 55 21

흔히 볼 수 있는 Layered Architecture 로 구성이 되어있다.

근데 참조가 너무 많은거다. 😭 이 상황에서 퍼사드 패턴을 떠올렸다. 🤔

구성

그래서 Controller -> Facade -> Service -> Repository 로 의존의 흐름을 넘기는 것을 생각했다.

그래서 여러 서비스 클래스를 한데모아 Facade에서 각 서비스들을 조합해서 붙여주는 식으로 정리를 했더니

의존 방향도 틀이 맞춰지고, 서비스단과 리파지토리 단위테스트를 쉽게 가져갈 수 있게 되었다.

@RestController
public class Controller {
    private final Facade facade;

    public Controller(final Service service) {
        this.service = service;
    }
}

@Facade // 어노테이션을 새로 정의해주었다
public class Facade {
    private final Service1 service1;
    private final Service2 service2;
    private final Service3 service3;

    public Facade(final Service1 service1, final Service2 service2, final Service3 service3) {
        this.service1 = service1;
        this.service2 = service2;
        this.service3 = service3;
    }
}

// 이런게 3개 있다.
public class Service1 {
    private final Repository1 repository1;

    public Service1(final Repository1 repository1) {
        this.repository1 = repository1;
    }
}

아무튼 이렇게 구성을 하게 되었다.

결과

결국 전체가 더러워질 바에는 서비스가 더러워져선 안된다고 생각을 했고,
많은 사람들에게 자문을 구한 결과도 다들 방금 한 얘기를 다들 하셨다. 감사합니다~ 🙏
무언가를 호출해서 모아주는 집약체인 퍼사드 클래스가 더러워지게 된거다.
기존 코드에 비해 엄청나게 개선 되었다고 생각한다. 중구난방인 의존을 한데 모았기 때문이다.
아무튼 한달반동안 많이 성장한것 같고 더 성장해야겠다.
갈 길이 멀고 나는 아직 배가 고프다. 더 많이 더 빨리 더 높게 성장하고싶다 👊 🔥🔥🔥🔥

728x90

'Diary' 카테고리의 다른 글

ATDD, 클린 코드 with Spring 5기 수료 회고  (0) 2022.08.14
블로그를 옮기고 최신 근황  (0) 2022.08.13
업무 회고  (0) 2022.08.07
1개월 1일 1커밋 회고  (0) 2022.08.07

+ Recent posts