본문 바로가기
개발 잡담

테이블 설계를 잘 했다면 반복자 패턴을 사용하지 않아도 되었을 텐데!^^

by 휴일이 2024. 3. 15.

 

 

테이블 설계를 잘못했다.

테이블을 필요 이상으로 많이 놔눠서 조회가 너무 힘든 상황이 발생했다..

 

 

직접 회사 코드를 오픈하거나 할 순 없으니

비유를 햄버거로 들어야겠다. 난 햄버거를 좋아하니깐.

 

 

이런 느낌으로다가

 

 

 

그런데 세트 메뉴를 조회하려고 할 때가 문제였다.

 

 

세트 메뉴 1번의 3번째 감자튀김 정보를 전부 조회하려면

 

세트메뉴 1번의 3번째 포장박스를 가져와서

3번째 포장 박스를 알고 있는 감자튀김을 하나하나 포장 박스에 담은 다음

세트 메뉴에 넣어야 하는 것이다....

 

 

근데 세트메뉴를 한 두 개 가져와야 하는 게 아니라 상당히 많았다^^^

 

 

 

 

이런 느낌 ^_^

고객 1이 세트 메뉴를 하나만 먹었을 리 없자나?

여러 개 먹었겠지?

게다가 감자튀김이 없는 주문이 있을 수도 있으니까 감튀가 없으면 감튀는 비어 있는 상태로 가져와야한다. ^_^;;;;;;;;;

 

 

그런데 각 객체는

고객 1에 대한 정보를 한꺼번에 List 로 가지고 있다.

게다가 그 감자튀김은 순서대로 넣어야함;;;;

 

 

그래서 그냥 for 문으로 돌리면..

- 세트메뉴1에 대한 포장박스를 찾고 (first for) 까진 괜찮은데

- 포장 박스에 대한 감자튀김을 리스트를 "전부" 돌려서 찾아와야함.. (second for)

 

물론 저렇게 구현해도 되긴 하지만

이미 필요한 감자튀김을 전부 찾았는데도, 불필요하게 다른 감자튀김까지 들여다봐야하기때문에 괜히 성능이 떨어지는 짓임..

개발자 이름빨 있는데 효율적으로 개발해야하지 않겠는가? ㅎㅅㅎ (아님말구;;)

 

 

 

생각을 했다.

2번용 감자튀김이 나오면

반복을 멈추고2번 박스에 담을 수 있는 법은 없을까?

 

 

 

내가 생각한 방법은 두가지였는데

그 중 한 가지 방법인 "Iterator" 를 쓰기로 했다.

 

 

포장박스 번호별로 박스와 감자튀김을 정렬시킨 뒤

감자튀김을 하나씩 꺼내온다.

2번 포장박스의 감자튀김이 나왔을 경우

반복을 멈추고 포장박스 2를 꺼내와서 2번 상자에 맞는 감자튀김을 담으면 된다

 

 

 

 

코드를 보자....-ㅅ-

public List<SetMenu> getManagementfriedPotatos(Long setMenuId, Integer boxCount) {
    SetMenuRepository.findById(setMenuId)
            .orElseThrow(SetMenuErrorCode.SET_MENU_NOT_FOUND::defaultException);

    List<Box> BoxList =
            BoxRepository
                    .findAllBysetMenuIdAndBoxCountBetween
                            // 1 번째 부터 요청한 박스까지
                            (setMenuId, 1, boxCount);

    if (BoxList.isEmpty()) throw new SetMenuException(SetMenuErrorCode.Box_NO_CONTENT);

    List<Long> BoxIdList = BoxList.stream()
            .map(Box::getId)
            .toList();

    List<FriedPotato> FriedPotatoList = FriedPotatoRepository.findAllByBoxIdIn(BoxIdList);

    BoxList.sort(Comparator.comparing(Box::getId).reversed());
    FriedPotatoList.sort(
            Comparator.comparing(FriedPotato::getBoxId, Comparator.reverseBox())
            .thenComparing(FriedPotato::getfriedPotatoSession, Comparator.reverseBox()));

    List<SetMenu> resultList = new ArrayList<>();

    Iterator<Box> friedPotatoIterator = BoxList.iterator();
    Iterator<FriedPotato> friedPotatoIterator = friedPotatoList.iterator();

    boolean flag = false;

    FriedPotato friedPotato = friedPotatoIterator.hasNext() ? friedPotatoIterator.next() : null;

    while (friedPotatoIterator.hasNext()) {
        List<FriedPotatoResDto> friedPotatoList = new ArrayList<>();
        Box box = friedPotatoIterator.next();
        if (friedPotato != null && box.getId().equals(friedPotato.getBoxId())) {
            flag = true;
        }

        while (flag) {
            if (!friedPotatoIterator.hasNext()) flag = false;

            friedPotatoList.add(FriedPotatoResDto.of(friedPotato));

            if (friedPotatoIterator.hasNext()) {
                friedPotato = friedPotatoIterator.next();
                if (!box.getId().equals(friedPotato.getBoxId())) {
                    flag = false;
                }
            }
        }
        resultList.add(SetMenu.of(box, friedPotatoList));
    }

    return resultList;
}

 

 

 

 

    SetMenuRepository.findById(setMenuId)
            .orElseThrow(SetMenuErrorCode.SET_MENU_NOT_FOUND::defaultException);

    List<Box> BoxList =
            BoxRepository
                    .findAllBysetMenuIdAndBoxCountBetween
                            // 1 번째 부터 요청 회차까지 전부
                            (setMenuId, 1, boxCount);

    if (BoxList.isEmpty()) throw new SetMenuException(SetMenuErrorCode.Box_NO_CONTENT);

    List<Long> BoxIdList = BoxList.stream()
            .map(Box::getId)
            .toList();

    List<FriedPotato> FriedPotatoList = FriedPotatoRepository.findAllByBoxIdIn(BoxIdList);

 

우리는 다 논리적 연관관계로 구성되어있기 때문에... 일단 유효한 아이디로 세트 메뉴를 요청했는지 확인한다.

그리고 박스를 가져와야하는데,

그 세트메뉴에 해당하는 박스를 1번부터 ~ 요청한 것까지 가져온당.

 

세트메뉴에 박스가 존재한다면

박스 아이디를 전부 꺼내와서 -> 박스들에 알맞은 감자튀김들을 가져온다.

 

 

BoxList.sort(Comparator.comparing(Box::getId).reversed());
    FriedPotatoList.sort(
            Comparator.comparing(FriedPotato::getBoxId, Comparator.reverseOrder())
            .thenComparing(FriedPotato::getfriedPotatoNumber, Comparator.reverseOrder()));

 

그리고 박스와 감자튀김을 아이디 기준으로 정렬한다.

특히 감자튀김은 순서대로 넣어야하기 때문에

1. 박스 기준으로 정렬한 후

2. 감자튀김 순서로 정렬해준당.

 

 

 

Iterator<Box> boxIterator = BoxList.iterator();
Iterator<FriedPotato> friedPotatoIterator = friedPotatoList.iterator();

 

그 다음 리스트를 우리의 사랑스러운 Iterator 객체로 만든다. ^)^/

 

 

// 감자튀김을 계속 넣을까 말까?
boolean flag = false;

    FriedPotato friedPotato = friedPotatoIterator.hasNext() ? friedPotatoIterator.next() : null;

    while (boxIterator.hasNext()) {
        List<FriedPotatoResDto> friedPotatoList = new ArrayList<>();
        Box box = boxIterator.next();
        if (friedPotato != null && box.getId().equals(friedPotato.getBoxId())) {
            flag = true;
        }

        while (flag) {
            friedPotatoList.add(FriedPotatoResDto.of(friedPotato));
            
            if (!friedPotatoIterator.hasNext()) flag = false;
            
            if (friedPotatoIterator.hasNext()) {
                friedPotato = friedPotatoIterator.next();
                if (!box.getId().equals(friedPotato.getBoxId())) {
                    flag = false;
                }
            }
        }
        resultList.add(SetMenu.of(box, friedPotatoList));
    }

 

flag 는 감자튀김을 계속 넣을까 말까? 판단하는 논리형~ㅎㅅㅎ

 

한 박스당 감자튀김은 있을수도 없을수도 있어서 감튀 : null 로 넣어준다.

 

박스가 없어질 때까지 while 문을 돌려주는데

감튀가 존재하고, 박스 식별자와 감자튀김이 가진 박스 식별자가 동일하다면 감튀를 돌려주러 flag = true ㄱㄱ

 

감튀를 넣는 while 문 진입한 후

감튀를 리스트에 넣어서 보관해줌ㅠㅠㅠ!!!

 

그리고 감튀가 없으면 다시 반복하지 않도록 막아줌

감튀가 있으면 다음 감튀로 함 돌려봐~

다음 감튀를 확인해서 박스에 맞지 않는 감튀면 더 이상 반복하지 않게 막아준당.

 

 

그리고 마지막에 한 박스에 해당하는 감튀를 전부 넣으면

세트메뉴리스트에 넣어서 보관해줌 ㅎㅅㅎ/

 

 

그러면 결과는

 

 

[
    {
        "세트식별자” : 3,
        "박스식별자“ : 3,
        “감자튀김”: []
    },
    {
        "세트식별자": 2,
        "박스식별자": 2,
        “감자튀김”: [
            {
                "감자번호": 3,
                "종류": “미국감자”
            },
            {
                "감자번호": 2,
                "종류": "수미감자"
            },
          	"감자번호": 1,
                "종류": “미국감자”
            }
        ]
    },
    {
        "세트식별자": 1,
        "박스식별자": 2,
        "감자튀김": [
            {
                "감자번호": 3,
                "종류": "수미감자"
            },
		"감자번호": 2,
                "종류": “강원도감자”
            },
            {
                "감자번호": 1,
                "종류": "수미감자"
            }
        ]
    }
]

 

이렇게 맛있는 감자 파티파티 !!

 

 

 

 

난 알고리즘 공부를 안했었으니까

이런 방법을 쓰면 될 거 같은데 이게 자바로 구현 가능한 거임? -> 이런 걸 찾아보는데

전에 디자인 패턴 책에서 반복자 패턴(Iterator)을 본 적이 있어서 급 생각나서

이걸로 되겠다 ! 하고 사용해서 구현해봄 ㅎㅎ

 

알고리즘이 꼭 코테만 아니어도 개발 인생에 필요한 것 같긴 하넹...

근데 이거 알고리즘 아닌가? (잘모름ㅋㅋ)

요즘 알고리즘 공부를 시작...했는데 야근이 넘 많아서 못하고 있었는데

야근 좀 덜하게 되면 해야지ㅠㅠㅠㅠㅠ흑흑

 

 

근데 결론은 그냥

박스 테이블에 감자를 넣었으면 해결되는 일인데

박스랑 감자 테이블 설계를 이상하게 해서 초래한 결과..

앞으로 테이블 설계할 땐 기획을 좀 더 생각하고 만들어야겠다 ㅎㅎ ㅠ 슬프다

728x90