반응형

 

 

본 게시물에서는, 코드 리뷰의 방법론에 대해서 다루지 않고,

깃허브의 VS Code 인터페이스 기능을 활용해

코드 리뷰를 진행하는 방법에 대해 간단하게 다뤄보겠습니다.

 

 

 


1. 코드리뷰를 원하는 PR에 접속

제가 참여했던 우테코 프리코스 미션 PR입니다.

 

 

2. "."(온점) 클릭하여 VS Code 인터페이스 화면으로 이동

위 화면 좌측 메뉴 버튼을 보시면, PR 버튼이 활성화되어 있음을 확인하실 수 있습니다.

코드의 삭제, 추가 내역을 보면서 코멘트를 달 수 있습니다.

 

아래에는 커밋 별 기록도 확인할 수 있습니다.

 

 

코드의 변화 내역을 굳이 보지 않아도 된다면,

좌측 맨 위에서 2번째 버튼을 클릭해 파일의 디렉토리 구조와 코드만 확인할 수 있습니다.

전체 코드에 대한 리뷰를 남기려면 이 메뉴가 가장 편합니다.

 

 

3. 코멘트 남기기

코드에 마우스를 가져다대면 나오는 + 버튼을 클릭합니다.

 

코멘트를 작성하고, '검토 시작' 버튼을 클릭합니다.

'댓글 추가' 를 클릭하면, 코멘트가 즉시 추가됩니다.

하지만 '검토 시작' 을 클릭하면,

추후에 PR 전체에 대한 코멘트를 남기고 submit 시 모든 코멘트가 동시에 저장됩니다.

이 방식이 가장 가독성이 좋으므로, 그렇게 진행해보겠습니다.

 

추가 코멘트들은 '댓글 추가'를 클릭하면 됩니다.

 

 

4. 리뷰 저장하기

아무 코드나 클릭해 코멘트 작성 에디터를 열고,

'리뷰로 이동' 을 클릭합니다.

 

이전에 달았던(보류해두었던) 코멘트들이 묶여있는 걸 확인할 수 있습니다.

하단에 PR 전체에 대한 코멘트를 남기고,

'Submit Review'를 눌러 리뷰를 제출합니다.

 

이후 뒤로 가기 버튼을 눌러 VS Code 인터페이스에서 벗어나면,

VS Code 인터페이스에서 작성한 리뷰를 확인할 수 있습니다.

보이지 않는다면 새로고침을 해주세요.

 

 

 

 


VS Code 인터페이스에서는

Command(Ctrl) + 좌클릭 과 같은 커맨드와 (메서드, 클래스로 이동)

검색 기능 등

코드를 보며 활용할 수 있는 다양한 기능을 사용할 수 있어 리뷰가 무척 편해집니다.

 

VS Code 인터페이스의 편리한 기능을 통해

코드에만 집중하는 것에 도움이 되셨길 바랍니다.

 

 

 

 

 

 

 

반응형

'Git' 카테고리의 다른 글

깃허브 리드미(프로필) 꾸미기 - 초초초 간단 버전  (0) 2022.10.20
반응형

썸네일용 테스트 성공 화면. 수고했다 나 자신..

프리코스 4주간의 대장정은 끝이 났고,

전 세계는 프리코스 참가자들을 축하하며 '월드컵'이라는 축제를 열었다(?)

 

늘 다음 미션에 대한 기대감 반, 두려움 반이 공존된 상태로 회고를 적곤 했었는데,

기대와 두려움이 모두 사라진 평온~한 상태로 회고를 적자니 조금 어색하기도 하다.

 

쉼 없이 달려왔기에, 스스로에게 잠깐의 휴식을 주고 싶은데..

미뤄둔 할 일이 많아 쉽지 않다.

 

그런 와중에도, 4주 차 미션을 통해 배운 많은 것들에 대한 기억이 휘발되기 전에

회고를 적으려고 블로그를 켰다.

 

회고 시작시작!

 

 

목차

- 객체를 분리하다 MVC를 깨닫다

- 리팩터링이란 몰까..?

- 자연스러운 TDD

- 예외처리와 자만

- 프리코스 커뮤니티

 

 


객체를 분리하다 MVC를 깨닫다

3주 차에서 MVC 패턴이라는 나에게 어려운 개념을 지워버리고, 객체 간 관계에만 집중했었다.

그러다 보니 나도 모르게 Lotto 클래스와 같은 도메인 로직에서 입출력 클래스를 호출하곤 했다.

스스로의 코드를 읽어보며, 도메인 로직과 UI 로직이 완전히 분리된 것이 아니라는 생각을 했다.

 

이번 4주 차 미션에서는

"BridgeGame 클래스에서 InputView와 OutputView를 사용하지 않는다"

라는 요구사항이 적혀 있었다.

 

도메인과 UI 로직이 서로를 의존하지 않게 하기 위함이라고 생각했다.

그런데.. 도메인의 내용을 출력하거나, 입력을 받아 도메인 로직에서 사용하려면 결국 연결이 되어야 한다.

근데 도메인과 UI는 분리되어야 한다..

..

..

..

그래서 Controller가 생긴 것이구나!

라는 깨달음을 얻었다.

 

사실 대단한 깨달음도 아니고, MVC 패턴 관련 아티클을 정독하다 보면 자연스럽게 알 수 있는 내용이었지만,

아무리 아티클을 읽어도 나에겐 와닿지 않던 내용들이었다.

 

하지만 객체 간 관계, 도메인 로직과 UI 로직이라는 개념, 객체 간 의존 관계 등 객체에 대한 여러 개념을 이해하고 나니,

MVC 패턴이 왜 생겨났고, 어떻게 사용되어야 하는지 이해되었다.

그동안 나는 거꾸로 된 순서로 공부를 하고 있었다..!

 

정말 신기하고 뿌듯한 경험이었다. ⭐️

 

 

 

 

리팩터링이란 몰까..?

이전의 나에게 리팩터링이란 그저 '실수를 수정하는 행위'에 지나지 않았다.

 

하지만 이번 미션의 학습 목표 설명에

"먼저 기능이 작동하도록 구현하고, 리팩터링으로 코드를 개선해 보는 경험"

을 언급하셨다.

 

그 이유가 있을 게 분명했다.

그래서 나는 리팩터링이 왜 중요한지 그 의미를 생각하며 미션을 해결하려고 노력했다.

 

우선 내가 리팩터링을 좁은 의미로 생각한 이유를 고민했는데,

아마 코드를 수정하는 것이 두려워서였던 것 같다.

 

설계 없이 코드부터 냅다 작성하던 과거의 나에게, 코드 수정은 엄청 복잡한 일이었으리라..

하지만 지난 미션들을 해결하며, 유지보수가 훨씬 수월해졌음을 느꼈다.

용기를 갖고 많은 부분을 리팩터링 단계로 넘기리라 다짐했다.

 

그랬더니 생긴 이점들은,

기능 구현 시 흐름이 끊기지 않을 수 있었으며, 고민이 필요한 부분은 리팩터링 단계에서 충분히 고민할 수 있었다는 것이다.

이전에는 기능을 구현하면서 동시에 깊게 고민하고, 찾아보고, 공부하고.. 그러다 보면 프로그램 전체 flow가 헷갈리고..

이런 일이 많았지만,

리팩터링에 많은 부분을 넘기면서 훨씬 수월하게 기능 구현을 마칠 수 있었다.

 

몇 가지 리팩터링 한 부분이 기억에 남는데,

공통 피드백과 피어 리뷰 내용을 적용한 것이다.

공통 피드백의 객체를 객체스럽게 사용하는 방법, final 키워드 사용 방법,

그리고 피어 리뷰에서 배운 String 객체를 리터럴 방식으로 선언할 때의 이점 등을 공부한 후에 코드에 녹일 수 있었다.

 

그리고 30줄짜리 메서드를 10줄로 분리했던 경험도 생생하게 떠오르고, (분기되는 부분, 공통되는 부분을 기준으로 메서드 분리..!)

다리 현황을 기록하는 객체를 Monitor라고 칭했는데, Drawer(다리 그림을 기록하는 객체)와 picture(그림으로 된 다리 이동 기록)로 표현을 수정한 것도 기억에 남는다.

 

 

 

 

자연스러운 TDD

TDD가 자연스러웠다니.. 도저히 믿어지지 않는 일이다.

2주 차 요구사항으로 테스트 작성이 추가되면서, 넌지시 알고 있던 TDD 방식을 적용해보았다.

 

테스트 코드가 기능 구현 시 나침반, 설계도와 같은 역할을 해준다는 사실을 깨달았긴 하지만..

테스트 구현이 익숙하지 않은 채로 TDD 방식을 사용하는 것은 너무 고통스러웠다.

 

그런데 4주 차 기능을 구현할 때는,

아주 자연스럽게.. 테스트 코드에 먼저 손이 갔다.

 

객체와 책임을 먼저 설계했고, 테스트 케이스 구현이 익숙했기 때문에 가능했던 일이라고 생각한다.

추후에 스프링과 같은 프레임워크를 공부하더라도, 처음부터 TDD를 고집하면 굉장히 괴로울 것이다.

하지만, 기본적인 구조와 기능을 익히고 자연스럽게 TDD를 사용할 수 있다면 굉장한 무기가 되어줄 것이라고 생각했다.

 

기본을 공부하며 응용을 깨달은 이 경험이, 앞으로 나의 공부에 좋은 영향을 줄 것이라고 많이 기대된다.

 

 

 

 

예외 처리와 자만

지난 3주 차 미션의 최대 난제였던 "예외 처리 후 프로그램 종료" 문제를 해결한 후,

나는 예외 처리 개념에 통달했다는 자만에 빠졌다.

 

하지만 4주 차 미션의 요구사항

"사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다."

를 해결하면서 바로 겸손해졌다..

 

3주 차에서 사용한 방식대로,

예외를 최상위 메서드 한 곳에서 처리하는 방식으로는,

입력을 다시 받을 메서드를 호출할 방법이 없었다.

 

예외 처리를 발생하는 각 지점에서 수행하고,

입력 메서드를 재귀적으로 호출함으로써 요구 사항을 만족시킬 수 있었다.

반복문으로 해결하는 방법도 공부를 했지만,

가독성 측면에서 재귀적 호출이 낫다고 판단했다.

 

공부한 내용을 기억하고 + 나누고자 아티클도 작성해보았다.

 

[Java] 예외처리 반복 - 올바른 입력값을 받을 때까지 (try-catch)

프로그램을 작성하다 보면 예외를 처리한 후, 예외가 발생된 메서드를 다시 실행해야하는 경우가 있습니다. 그것을 수행하는 몇 가지 방법에 대해 정리해보고자 합니다. 예외 처리의 기본적인

woojin.tistory.com

 

자만이 깨지면서 느낀 점은..

하나의 지식은 하나의 문제로 얻을 수 없다는 것이었다.

여러 문제를 다뤄봐야 비로소 하나의 깊은 지식이 완성된다는 것을 깨달을 수 있었다.

(책 하나만 읽은 사람이 제일 어설프다 그런 것과도 비슷한..)

 

 

 

 

프리코스 커뮤니티

이번 주에도 커뮤니티로부터 많은 것을 얻으려고 노력했다.

  • 주간 회고록 공유
  • 동료분들의 회고 10개 정독하기 (사실 읽다 보니 20개는 읽은 듯)
  • 피어 리뷰 10개 작성

다만 시간이 좀 부족해, 지난주처럼 토론 글을 모두 정독하지는 못 했다.

 

지난 주차 동안 꾸준히 리뷰에 참여했더니, 리뷰 속도가 많이 개선되었다.

빨리 하는 것이 중요한 것은 아니지만, 기본적인 로직을 이해하는 속도가 빨라지자

다른 곳에서 여유를 가질 수 있었다.

 

지난 리뷰들은 요구사항과 관련된 실수, 네이밍에 관한 조언 등

비교적 단순한 코멘트를 달았다면,

이번 리뷰에서는 객체의 상태가 꼭 필요한지, UI 로직이 잘 분리되었는지와 같은 고차원적인 부분에 대한 의견을 전달할 수 있었다.

무엇보다, 동료분들의 일주일 간의 노고가 깃든 코드에서 빛나는 부분을 찾아볼 수 있는 여유가 생겼고

힘이 되는 칭찬을 드리기 위해 나름대로 노력했다. 그게 참 좋았다.

내가 받은 리뷰를 통해 많은 자신감을 얻었기 때문에, 돌려드리고 싶었기 때문.

 

회고를 읽는 것은 너무 재밌었다.

비슷한 고민이 담긴 글을 보고 위로를 많이 구할 수 있었다.

그리고 내가 해보지 못한 생각들로부터 많은 인사이트를 얻을 수 있었다.

특히, 실수가 두려움의 대상이 아닌 성장의 시작이라는 관점이 크게 와닿았다.

 

 

아쉬운 점이 하나 있었다면..

맞리뷰 오신다고 했는데 잠수 타신 분들이.. (세 분이나..)

늦게라도 와주실 거라 믿습니다 ^^.. 사실 안 믿고요

 

그래도 많은 분들께서 맞리뷰 해주셔서 감사했습니다!

 

 

 

 


끝이라니.. 믿어지지 않고.. 많이 허전하겠지만,

일단 월드컵을 볼 생각에 무척 설레고요,

새롭게 공부를 시작할 생각에도 설렙니다.

 

모두 행복하세요!

 

감사합니다.

 

 

 

 

 

 

 

 

 

 

반응형
반응형

 

 

 

프로그램을 작성하다 보면

예외를 처리한 후, 예외가 발생된 메서드를 다시 실행해야하는 경우가 있습니다.

그것을 수행하는 몇 가지 방법에 대해 정리해보고자 합니다.

 

예외 처리의 기본적인 원리나,

예외 처리 후 프로그램을 종료하는 로직에 대한 설명은

아래 아래클을 참고해주세요.

 

[Java] 간단한 예외처리(try catch) 원리 (feat. throws IllegalArgumentException은 왜 빨간 줄이 안 생길까?)

예외를 처리하다 보면, 아래와 같은 에러 문구를 쉽게 볼 수 있습니다. 바로 Unhandled exception 입니다. 예외 발생 예외의 종류는 다양하고, 예외가 발생하는 코드도 다양합니다. 본 아티클에서는,

woojin.tistory.com

 

 

 

 


문제 상황 1번 - 예외 처리 후 재실행

비즈니스 요구사항
1. 숫자를 입력 받고, 2를 곱한 수를 출력한다.
2. 숫자가 아닌 문자를 입력 받으면 [ERROR]로 시작하는 에러 메시지를 출력하고, 다시 입력 받는다.
// main 메서드를 호출하는 Practice 클래스
public class Practice {
    public static void main(String[] args) {
        Study study = new Study();
        study.multiple();
    }
}


// 비즈니스 로직을 구현한 Study 클래스
public class Study {
    public void multiple() {
        int number = readNumber();
        System.out.printf("%d에 2를 곱하면 %d 입니다.", number, number*2);
    }

    private int readNumber() {
        System.out.print("숫자를 입력해주세요 : ");
        Scanner scanner = new Scanner(System.in);
        int number = scanner.nextInt();
        return number;
    }
}

위의 코드는, 숫자를 입력 받아 2를 곱한 출력을 보여주는 로직을 구현한 것입니다.

출력은 아래 사진과 같습니다.

 

그런데, 숫자가 아닌 문자를 입력한다면 어떻게 될까요?

InputMismatchException이 발생하고 프로그램이 종료됩니다.

 

비즈니스 요구사항을 만족하려면

에러 메시지를 출력하고, 입력을 다시 받아야 합니다.

아래와 같이 코드를 수정하겠습니다.

 

// 재귀적 호출
public class Study {
    public void multiple() {
        try {
            int number = readNumber();	// 1번
            System.out.printf("%d에 2를 곱하면 %d 입니다.", number, number*2);	// 2번
        } catch (InputMismatchException e) {	// 3번
            System.out.println("[ERROR] 숫자를 입력해주세요.");	// 4번
            multiple();	// 5번
        }
    }

    private int readNumber() {
        System.out.print("숫자를 입력해주세요 : ");
        Scanner scanner = new Scanner(System.in);
        int number = scanner.nextInt();
        return number;
    }
}

예외는 readNumber()에서 발생합니다.

그것을 호출하는 multiple()try-catch를 적용하고,

재귀적으로 multiple()을 다시 호출했습니다.

 

즉, 1번에서 예외가 발생하면

2번을 건너뛰고

3번에서 예외를 catch합니다.

그리고 4번, 5번 문장이 실행됩니다.

 

하지만 재귀적 호출 방식에는 치명적인 단점이 있습니다.

메모리에 스택 방식으로 메서드 호출 명령이 저장되지만,

호출된 메서드가 종료되지 않으면 메모리에 명령이 계속 쌓이게 되어

스택오버플로우(SOF) 현상이 발생할 수 있습니다.

 

사실 위의 예시 코드는 재귀적 호출 시 별도의 연산을 하지 않고,

함수를 호출하기만 하는 방식입니다.

그래서 SOF의 부담이 적기는 하지만, 호출된 깊이만큼 메모리에 스택이 쌓이는 것은 마찬가지입니다.

호출되는 로직이 변경되는 경우도 있을 수 있습니다.

 

따라서 재귀적 호출의 위험을 없애기 위해,

반복문을 통해서도 문제를 해결할 수 있습니다.

// 반복문 사용
public class Study {
    public void multiple() {
        while (true) {
            try {
                int number = readNumber();
                System.out.printf("%d에 2를 곱하면 %d 입니다.", number, number * 2);
                break;
            } catch (InputMismatchException e) {
                System.out.println("[ERROR] 숫자를 입력해주세요.");
            }
        }
    }

    private int readNumber() {
        System.out.print("숫자를 입력해주세요 : ");
        Scanner scanner = new Scanner(System.in);
        int number = scanner.nextInt();
        return number;
    }
}

catch문에서 multiple()을 다시 호출하지 않고,

try문이 온전히 실행되어 break 될 때까지

try-catch문을 반복합니다.

 

 

multiple()이 아닌 readNumber()에서 try-catch를 할 수도 있습니다.

 

다만, readNumber()의 반환 타입은 int이기 때문에

catch문에서는 두 가지 중 하나를 실행해야 합니다.

1. int를 반환한다.

2. Throwble 객체를 throw 한다.

 

입력을 다시 받지 않고, catch 문에서 int를 반환하는 것은 어색합니다.

Throwble 객체를 throw한다면, 결국 상위 메서드에서 try-catch를 통해 예외를 처리해야 합니다.

따라서 readNumber()가 아닌 상위 메서드 multiple()에서 예외 처리를 진행했습니다.

 

 

 

 

 

문제 상황 2번 - 특정 예외를 발생시킨 후 재실행

비즈니스 요구사항
1. 숫자를 입력 받고, 2를 곱한 수를 출력한다.
2. 숫자가 아닌 문자를 입력 받으면 IllegalArgumentException을 발생시키고, [ERROR]로 시작하는 에러 메시지를 출력한 후, 다시 입력 받는다.

이번에는 특정 예외를 발생하라는 요구사항이 추가되었습니다.

기존에는 readNumber()scanner.nextInt()라는 구문에서 InputMismatchException이 발생했습니다.

이번에는 이를 IllegalArgumentException으로 바꿔서 예외를 발생시켜보겠습니다.

 

// 재귀적 호출
public class Study {
    public void multiple() {
        try {
            int number = readNumber();
            System.out.printf("%d에 2를 곱하면 %d 입니다.", number, number * 2);
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
            multiple();
        }
    }

    private int readNumber() {
        try {
            System.out.print("숫자를 입력해주세요 : ");
            Scanner scanner = new Scanner(System.in);
            int number = scanner.nextInt();
            return number;
        } catch (InputMismatchException e) {
            throw new IllegalArgumentException("[ERROR] 숫자를 입력해주세요.");
        }

    }
}

이번에는 readNumber() 내부에서 발생하는 InputMismatchExceptioncatch한 후 IllegalArgumentException을 발생시켰습니다.

그리고 상위 메서드인 multiple에서 그것을 catch한 후, 다시 multiple을 재귀적으로 호출합니다.

 

 

이 또한, 재귀적 호출로 인한 SOF의 위험에서 벗어나기 위해

반복문을 사용할 수 있습니다!

 

위의 문제 상황 1번을 참고하여, 직접 반복문으로 수정해보신다면

예외 처리에 대한 이해도가 높아지시리라 생각됩니다..만

 

아래에 반복문으로 수정한 코드를 공유드립니다.

 

 

// 반복문 사용
public class Study {
    public void multiple() {
        while (true) {
            try {
                int number = readNumber();
                System.out.printf("%d에 2를 곱하면 %d 입니다.", number, number * 2);
                break;
            } catch (IllegalArgumentException e) {
                System.out.println("[ERROR] 숫자를 입력해주세요.");
            }
        }
    }

    private int readNumber() {
        try {
            System.out.print("숫자를 입력해주세요 : ");
            Scanner scanner = new Scanner(System.in);
            int number = scanner.nextInt();
            return number;
        } catch (InputMismatchException e) {
            throw new IllegalArgumentException("[ERROR] 숫자를 입력해주세요.");
        }

    }
}

 

 

 

 

 


이렇게 예외가 발생했을 때, 예외가 발생한 메서드를 반복하는 로직을 구현해보았습니다.

 

1. 재귀적 호출

2. 반복문 사용

 

위의 두가지 방법으로 로직을 구현할 수 있었는데,

각각의 장단점이 있습니다.

 

재귀적인 호출은 반복문을 사용하지 않아 들여쓰기가 깊어지지 않는다는 장점이 있습니다.

또한 메모리에 명령 호출 스택이 쌓여, SOF(Stack OverFlow)가 발생할 수 있습니다.

반복문은 들여쓰기가 깊어지지만, 메서드 내부에서 반복을 원하는 부분만 반복할 수 있습니다.

 

가독성은 재귀적 호출이, 성능은 반복문 사용이 낫다고 볼 수 있습니다.

 

 

 

우리 인생도 수많은 예외가 발생하곤 합니다.

능숙하게 처리하고 목표를 이뤄내는 법을 익혀야겠다는 생각을 합니다.

 

감사합니다.

 

 

 

 

 

 

 

 

 

반응형
반응형

책 소개

객체지향을 공부한다면, 누구나 한 번 쯤 읽어보면 좋을 책.

 

객체지향의 개념을 처음 접하는 사람도 쉽게 이해할 수 있는 설명과 예시로 이루어져 있고,

객체지향의 개념을 실무에서 적용해본 사람도, 새로운 인사이트를 얻어갈 수 있는 책이라고 생각합니다.

 

개인적으로는 반복되는 설명이 많아 읽는 데에 피로감이 다소 있었습니다만,

어느 모로 보나 쉽고, 재밌고, 유익한 책인 것만은 확실합니다.

 

 

저는 객체지향 개념에서 사용되는 단어들,

예컨대 책임, 상태, 추상화, 캡슐화 등..

단어만으로는 뜻이 와닿았던 적이 없었습니다.

 

하지만 모방이 아닌 은유(저는 차용이라고도 이해했습니다)를 통해

실세계의 개념을 객체지향에 적용하고,

객체지향만의 고유의 특징을 이해하며, (음료수가 스스로 마셔질 수 있다..!)

그 안에서 어려운 단어들을 풀어서 설명해주신 방식이 너무 좋았습니다.

 

아직 갈 길이 멀었지만,

그래도 내가 객체지향을 하고 있구나 라는 생각은 들게 된 것 같습니다.

 

 

아래에서는 책의 내용을 되짚어보고자 합니다.

 

 

 

 


책 내용 상세 정리

(* 편의상 반말을 사용함을 양해 부탁드립니다.)

 

 

1장 - 협력하는 객체들의 공동체

객체지향은 역할, 책임, 협력으로 이루어진다.

 

[카페 예시]

  • 역할 : 손님, 캐셔, 바리스타
  • 책임 : 주문, 커피 오더, 커피 만들기
  • 협력 : 커피 주문 전 과정

객체지향이란 시스템을 ‘상호작용하는 자율적인 객체들의 공동체’로 바라보는 것

자율적인 객체란, 상태와 행동을 함께 지니며, 스스로를 책임지는 객체

 

각 역할은 협력하고, 역할은 관련된 책임의 집합.

 

객체 사이 상호작용은 ‘메시지’로만 이루어지고,

메시지를 수신한 객체는 적합한 메서드를 자율적으로 선택

 

 

 

 

2장 - 이상한 나라의 객체

객체는 상태, 행동, 식별자로 이루어져 있다.

 

'이상한 나라의 앨리스 예시'로 그것을 설명한다.

예시 내용을 요약하면 다음과 같다.

 

높이가 40cm인 문으로 나가 아름다운 정원에서 뛰어 놀고 싶은 앨리스.

앨리스에게 주어진 것은 '마시면 키가 줄어드는 음료수', '먹으면 몸이 커지는 케이크'이다.

 

 

상태

  • 엘리스의 키
  • 음료수의 양

행동

  • 음료수를 마시다
  • 케이크를 먹자
  • (음료수 입장에서) 마셔지다 → 실세계와 객체지향 세계가 다른 점!

식별자

  • 고유의 객체를 구별할 수 있는 값
  • 엘리스의 국적 + 주민번호 쯤이 되겠다..

 

캡슐화란?

  • 객체의 상태를 (캡슐 안에 숨겨두어) 외부로 노출하지 않는다.
  • 예를 들면, 앨리스 객체의 키에 alice.height 로는 접근할 수 없고, 앨리스 객체에 getHeight() 메서드가 있다면 그것으로 값을 구해올 수 는 있는 것.
  • 객체는 외부에 상태가 아닌 행동만을 노출하고, 외부에서는 행동만을 통해서 객체에 접근한다.

 

행동이 상태를 결정한다.

 

앨리스 예시에서, 키와 위치를 먼저 생각하는 것이 아니라,

행동을 먼저 생각하고 필요한 상태를 정의한다.

 

객체지향은 현실세계의 모방이 아닌 은유다!

객체지향 세계는 현실세계의 비유를 통해 쉽게 이해할 수 있다. (변수명을 지을 때도, 현실세계의 은유를 빌리면 훨씬 직관적이다!)

하지만 현실과 다른 무궁무진한 상상을 구현할 수 있으니 현실세계 사고에 갇히지 말자. (음료수가 스스로 마셔지는 것 또한 그렇다.)

--> 객체스럽게 설계하라!

 

 

 

 

3장 - 타입과 추상화

3장에서는 트럼프 카드의 몸체를 지닌 하트 여왕, 스페이드 정원사, 클로버 병사의 행렬을 발견하는 앨리스를 묘사한다.

 

 

추상화

명확한 이해를 위해, 특정 절차나 물체를 생략하거나 감춰 복잡도는 줄이는 방법.

  • 첫 번째 차원 : 공통점은 취하고 차이점은 버리는 일반화
  • 두 번째 차원 : 불필요한 세부 사항을 제거

개념

특정 객체가 어떤 그룹에 속하는지 결정하는 것

  • 비행기, 자동차, 사람 등..
  • 앨리스 예시 : 트럼프!

 

타입

컴퓨터공학에서의 개념이자, 타입의 관점에서 본 객체

  1. 객체가 타입에 속하는지 결정하는 것은 객체의 행동
  2. 객체들이 동일한 행동을 수행할 수 있다면, 동일한 타입
  3. 객체의 내부적 표현(행동의 구현 방식, 상태)은 외부로부터 감춰진다

 

타입의 계층

트럼프 - 트럼프 인간

트럼프는

  • 납작 엎드리기
  • 뒤집어지기

트럼프 인간은

  • 납작 엎드리기
  • 뒤집어지기
  • 걸을 때마다 몸이 종이처럼 좌우로 펄럭이기

트럼프는 트럼프 인간의 일반화(슈퍼타입)

트럼프 인간은 트럼프의 특수화(서브타입)

 

정적 모델과 동적 모델

객체는 동적이다 -> 시간에 따라 상태는 변한다

타입은 정적이다 -> 정적인 관점에서 객체를 묘사한다

결국 타입은 추상화다!

 

 

 

 

4장 - 역할,  책임,  협력

 

재판이라는 협력

  1. 왕이 판사로서 증인을 불러오라고 요청,
  2. 토끼는 증인인 모자 장수를 부름,
  3. 모자 장수는 법정에 입장,
  4. 왕은 모자 장수에게 증언을 요청,
  5. 모자 장수는 증언

객체지향 설계는

  1. 협력이라는 문맥 속에서 책임을 고민
  2. 책임을 적절한 객체에 부여

하는 순서로 작성되어야 한다.

 

역할

역할은 책임의 집합이다.

 

역할은 협력을 추상화하고, 유연한 객체지향 설계를 만든다.

 

판사 역할 : 왕, 왕비 수행 가능

증인 : 모자 장수, 공작 부인의 요리사, 앨리스 수행 가능

 

이처럼 ‘판사’, ‘증인’의 역햘로 구성된 ‘재판’이라는 협력은 추상화되어 있고, 유연한 설계이다.

 

객체지향 설계 기법

  • 책임 주도 설계
    • 책임들을 식별하고, 적합한 객체에게 할당하는 방식으로 설계
  • 디자인 패턴
    • 책임 주도 설계의 결과를 표현하는 것으로, 반복되는 문제 해결을 위한 방법과 절차를 만들어 놓은 틀
    ex: MVC, MVP, MVVM
  • 테스트 주도 개발 : 응집도가 높고 결합도가 낮은 클래스로 구성된 시스템을 개발할 수 있는 좋은 방식.

 

 

 

 

5장 - 책임과 메시지

 

책임

책임은 적당히 추상적이면서 구체적이어야 한다.

→ ‘증언하라’는 책임

책임은 how가 아닌 what을 설명

 

메시지

메시지 → 메서드 : 모자장수.증언하라(어제, 왕국)

 

모자장수 : 수신자

증언하라 : 메시지 이름

어제, 왕국 : 인자

 

같은 메시지를 보내도, 수신자가 다르게 결정할 수 있다. → 다형성!

 

What/Who 사이클 : 책임 주도 설계의 핵심 → 어떤 행위가 필요한지 결정하고, 누가 수행할 것인지를 결정한다.

 

묻지 말고 시켜라! (Don’t ask, 디미터 법칙) → 객체가 자율적으로 구현 → 캡슐화 보장, 결합도 낮게 유지

 

인터페이스와 구현

인터페이스 : 객체 외부

구현 : 객체 내부

인터페이스는 구현과 분리되어야 한다.

 

인터페이스의 3가지 원칙

  • 좀 더 추상적인 인터페이스 : 객체의 자율성 보장
  • 최소 인터페이스 : 객체 내부 동작에 대해 가능한 적은 정보만 노출
  • 인터페이스와 구현 간 차이를 인식

캡슐화 : 자율성 보존을 위해 구현을 외부로부터 감추는 것

데이터 캡슐화 : 상태와 행동을 하나의 단위로 묶음

 

자율적인 책임은!

  • 협력을 단순하게 만든다 (추상화)
  • 외부와 내부가 명확하게 분리 (캡슐화)
  • 내부를 변경해도 외부에 영향 X (변경의 파급효과가 객체 내부로 캡슐화 → 객체 간 결합도가 낮아짐)
  • 협력의 대상을 다양하게 선택할 수 있는 유연성 제공 (+ 재사용성 증가)
  • 객체 역할 이해가 쉬워짐 (응집도 증가)

 

 

 

6장 - 객체 지도

  • 소프트웨어 제품 설계 → 기능 설계 + 구조 설계

도메인 모델

  • 구조를 설계하기 위한 모델
  • 도메인 : 프로그램 사용 분야 (은행, 게임, 병원 등)
  • 모델 : 대상을 단순화한 구조
  • 도메인 모델 : 대상 영역의 지식을 단순하게 구조화한 것

계좌

  • 계좌번호
  • 예금액

이자

  • 금액
  • 지급일자

위의 도메인 모델은 코드에서 사용할 개념과 관계를 제공 -> 그것을 구현하여 코드 작성

 

Account 객체 설계

상태

  • accountNumber
  • amount

메서드

  • calculateInterest(when)

 

유스케이스

사용자와 시스템 간 상호작용을 텍스트로 정리한 것

 

예시)

유스케이스명 : 중도 해지 이자액을 계산한다

일차 액터 : 예금주

주요 성공 시나리오:

  1. 예금주가 정기예금 계좌를 선택한다.
  2. 시스템은 정기예금 계좌 정보를 보여준다.
  3. 예금주가 금일 기준으로 예금을 해지할 경우 지급받을 수 있는 이자 계산을 요청한다.
  4. 시스템은 중도 해지 시 지급받을 수 있는 이자를 계산한 후 결과를 사용자에게 제공한다.

확장 : 3a. 사용자는 해지 일자를 다른 일자로 입력할 수 있다.

 

유스케이스의 특징

  1. 텍스트다.
  2. 하나가 아닌 여러 시나리오들의 집합이다.
  3. 단순한 피처가 아닌, 연관된 기능의 묶음이다.
  4. 사용자 인터페이스와 관련된 세부 정보를 포함하면 안 된다.
  5. 내부 설계 정보를 포함하면 안 된다.

 

책임 주도 설계는

유스케이스 작성 + 도메인 모델 → 협력 관계 및 메시지 정의 → 코드 구현

의 순서로 진행되어야 한다.

 

 

 

 

7장 - 함께 모으기

 

개념 관점 : 도메인의 개념과 관계를 보는 관점

명세 관점 : 인터페이스 관점

구현 관점 : 코드 관점

코드에 각 관점이 잘 드러나게 설계해라

 

도메인 개념 참조 → 변화에 쉽게 대응 가능(안정적이고 이해가 쉽다)

인터페이스와 구현을 분리하라! → 변화에 유연하게 하라!

 

 

 

 


 

 

 

감사합니다!

 

 

 

 

 

반응형
반응형

비전공자 && 자바 어린이의 백엔드 프리코스 회고

 

 

지난 주차 소감에, 전례 없이 몰입하겠다고 다짐했던 기억이 난다.

다행히도 이번 주는 그 다짐을 지켰다. 후회가 남는 순간이 떠오르지 않을 만큼 최선을 다 했다.

 

그만큼이나 많은 것을 배우고 느낀 시간이었다. 어느 정도였냐면..

미션 제출 시에 소감을 작성해서 내야 하는데, 명시는 되지 않았지만 글자수 제한이 5,000자라는 것을 누군가 공유해주셨다.

1주차에 그것을 듣고 "5,000자를 쓰는 사람이 있어..? 너무 늘여쓴 거 아닌가.."라고 생각했는데

이번 주의 내 소감 초안은 6,000자였다..

자소서를 다듬듯이 수정해서 글자수를 맞추고 제출했다는..

 

그리고, 여러번 검토해서 실수 없다고 생각했는데 제출하고 보니

"첫째로, 결과를 통해 과정을 돌이켜본다는 것입니다.."

라고 적은 것을 발견했다.

왜 아련한 건데..

 

아무튼 회고 시작!


기술적인 배움

 

클래스(객체) 분리

MVC를 흉내냈던 지난 주차와는 달리, '객체'에만 집중해서 프로그램을 설계했다.

M(model)도 잘 모르면서 MVC를 쓰던 나였는데,

코수타에서 말씀해주신대로 요구사항에 집중해보기로 했다.

(역시 그것만으로도 벅찼다..)

 

'도메인' 로직과 'UI' 로직을 분리하라는 요구사항도 있었다.

'도메인'이라는 단어가 잘 와닿지 않아 따로 찾아보며 공부했고,

'프로그램이 해결하려는 문제, 또는 그것이 속한 분야'라고 나름대로 정의를 했다.

 

객체를 설계할 때는 현실세계의 개념을 차용했다.

프리코스를 진행하며 '객체지향' 개념을 처음 제대로 공부했고,

'객체지향의 사실과 오해'라는 책의 도움을 많이 받았다.

이번 미션에서는, 책에서 배운 내용을 제대로 써먹을 수 있었다.

 

특히, 핵심 책임(행동)을 먼저 떠올리고,

  • 입력한 금액 만큼의 로또를 발행한다.
  • 로또의 당첨 여부를 계산한다.
  • 총 로또의 당첨 결과를 계산한다.

책임을 위해 오고갈 메시지에 집중했다.

 

그렇게 도메인 모델을 설계할 수 있었다.

지금 다시 보니 부족한 부분이 많이 보이지만..

그래도 내가 이런 다이어그램을 손으로 그렸다는 것 자체가 뿌듯했다.

필요한 정보들이 많이 부족할 수도 있지만..

핵심 메시지만 간결하게 담아, 읽히기 쉽다는 장점은 확실히 있는 듯하다.

 

이후에는 기능 구현 목록을 자세히 작성하고, 구현을 시작했다!

 

아무튼, 이렇게 설계에 공을 들이고나서 구현을 시작하니, 거칠 것이 없었다. (사실 많았다..)

 

 

 

 

도메인 로직에 대한 단위 테스트

지난 미션을 통해 '테스트 케이스 구현'에 대해서 익숙해질 수 있었다면,

이번 미션을 통해서는, 테스트의 의미와 설계, 구조와 리팩토링 등 전반적인 실력을 향상시킬 수 있었다.

 

그에 앞서, 지난 주의 나는 public, private, default의 뜻만 알고,

그것을 어떻게 써야하는지 전혀 몰랐다.

 대부분의 메서드가 default로 선언된 것만 봐도 그렇다..

동료분께서 남겨주신 피어 리뷰의 일부.. 접근 제어자를 왜 안 썼냐면.. 몰라서요.. ㅋㅋ......민망..

 

하지만 피어 리뷰와 아고라 정독을 통해 관련 키워드를 공부할 수 있었고,

이번 주에는 public 메서드와 그것을 뒷받침하는 private 메서드를 분리해서 구현했다.

하지만 private 메서드를 어느 범위까지 설정하고, public 메서드 테스트로 대체해야하는지 정확한 감이 없었다.

--> 3주차 공통 피드백에서, private 메서드 사용 범위에 대해 짚어주셨다.. 나름대로 소름이 조금 돋았다..

 

또, 2주차 미션에서는 테스트 코드의 가독성이나 컨벤션을 전혀 고려하지 않았다.

하지만 3주차 미션에서는 구현이 익숙해지니 가독성과 유지보수 같은 것들을 신경쓸 수 있었다.

 

should_기대결과_When_테스트상태 컨벤션으로 메서드명을 수정했다.

물론 DisplayName이라는 어노테이션으로 테스트 케이스 설명을 작성할 수 있지만,

메서드명을 아무 의미 없이 흘려보내지 않고, 직관적으로 이해할 수 있도록 작성하는 것은 의미가 크다고 생각했다.

 

BDD 패턴의 일종인 'given, when, then' 패턴도 적용해보았다.

테스트 코드 가독성 뿐만 아니라, 테스트 케이스의 목적을 스스로 정확히 이해하는 데에도 도움을 주었다.

직관적인 코드는 언제나 득이 된다.

 

 

그리고, 공통 피드백에 언급되었던 내용대로

테스트 코드를 작성하는 이유에 대해 고민해보았다.

 

기능을 빠르게 검증할 수 있어서 구현 당시에도, 리팩토링 시에도 큰 도움을 받을 수 있는 것은

지난 주에 이미 절실히 깨달았다. 그리고 그 외에도 느낀 점이 많았다.

 

첫째로, 결과를 통해 과정을 돌이켜볼 수 있다는 것이다.

기능을 구현할 때는, 내가 알고 있는 이론들로 가설을 세우고 코드로 표현한다. 거기에 더해 테스트 코드를 작성하면, 기능 요구 사항을 토대로 다양한 케이스를 검증할 수 있다. 결과를 토대로 프로덕션 코드의 과정을 되돌아 볼 수 있는 것이다.

 

마치, 경제학의 이론을 세우는 것이 구현이라면, 그것이 시장에서 적용이 되는지 확인하는 것은 테스트와 같다고 생각했다.

테스트를 구현하고 실행하면, 그 즉시 코드의 실용성을 많은 부분 파악할 수 있었고,

부족한 부분, 모순된 부분을 파악해 이론에 적용할 수 있었다.

 

둘째로, 학습을 위한 새로운 도구가 되어줬다는 것이다.

공통 피드백에 있던 내용인데, 테스트를 학습 도구로 사용했다.

기존에는 어떤 자료형과 메서드를 사용해보고 싶으면, 새로운 프로젝트를 만들어 실행시켰다.

여간 번거로운 일이 아니었다.

 

하지만, 테스트 코드로 원하는 기능을 작성하고 실행하면 간단하게 그 결과를 확인할 수 있었다.

거기에다 프로덕션 코드 작성 중 학습 기록을 아주 쉽게 참고할 수 있어 더욱 좋았다..

이런 생각을 여태 못 했다니..

 

특히 이번 미션에서는, 나를 아주 못살게 굴었던 수익률 계산을 연습할 때 큰 도움을 얻었다.

 

Math.round(), String.format() 등 다양한 메서드를 사용하다가

DecimalFormat이라는 녀석을 알게 되었고,

테스트 코드를 통해 쉽게 연습할 수 있었다!

 

 

종합하면,

테스트 코드는 기능을 검증하고, 촘촘한 기능 설계를 도우며, 학습을 위한 도구로도 사용이 가능하다는 것을 깨달았다.

설계, 작성, 학습, 유지보수까지, 개발 전 과정에 걸쳐 지대한 영향을 미치는 것이다!

 

 

 

 

예외 처리

메일로도 공지가 왔던 예외 처리 요구 사항을 구현하면서,

많은 지원자들이 겪던 문제를 나도 동일하게 겪었다.

사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 종료한다.

위의 요구사항이었는데,

try catch를 통해 IllegalArgumentException을 발생시키고, 콘솔에서도 확인했지만

테스트가 통과되지 않았다.

 

테스트 코드가 정의된 곳에서 발견한 NoSuchElementException을 발생시키니 테스트 통과는 되었지만,

의도한 바가 아니라는 것을 쉽게 알 수 있었다.

 

아무리 생각해도 방법이 떠오르지 않자.. 그냥 포기할까 생각도 했지만..

나는 프로그램이 종료되는 것이 아니라 '종료한다'는 표현에 집중했다.

 

그 결과! 나의 코드는 프로그램을 종료한 것이 아니라, 비정상적으로 종료된 것임을 깨달았다.

 

예외의 메시지를 출력하고, 예외를 발생시켜서 비정상 종료되는 것이 아니라!

예외 메시지를 출력하고, 더이상 실행할 명령이 없어 자연스럽게 프로그램을 종료하는 그림을 떠올렸다!

그 결과, main 메서드에 try catch를 적용함으로써 문제를 해결할 수 있었다..

 

와.. 이걸 깨달았을 때 카타르시스는 정말 잊을 수 없었다..

그리고 이 과정을 통해 예외처리에 대한 없던 기초가 세워졌다.

내 프로그래밍 인생 처음으로(길지도 않지만 ㅋㅋ), 예외처리 개념을 제대로 이해했다고 느꼈다.. 

(관련해서 블로그 아티클도 적었다!)

 

[Java] 간단한 예외처리(try catch) 원리 (feat. throws IllegalArgumentException은 왜 빨간 줄이 안 생길까?)

예외를 처리하다 보면, 아래와 같은 에러 문구를 쉽게 볼 수 있습니다. 바로 Unhandled exception 입니다. 예외 발생 예외의 종류는 다양하고, 예외가 발생하는 코드도 다양합니다. 본 아티클에서는,

woojin.tistory.com

 

 

 

 

프리코스 커뮤니티

이번 주차 목표 중 하나는, 프리코스 커뮤니티에서 얻을 수 있는 모든 것을 얻는 것이었다.

  • 주간 회고록 공유
  • 동료분들의 회고 10개 정독하기
  • 피어 리뷰 10개 작성
  • 토론 글 전부 정독
  • 토론 글 작성하기

위 내용을 목표로 나름대로 최선을 다해 커뮤니티 활동을 했다.

역시 넘치게 많은 것을 배울 수 있었다..

 

동료분들의 회고에서는, 프리코스를 바라보는 새로운 시각, 기술적인 배움을 구할 수 있었다.

 

피어 리뷰는 정말 기술적으로 크게 성장한 경험이었는데,

리뷰 경험이 거의 없던 나는 타인의 코드를 이해하는 것이 너무 벅찼다..

하지만 리뷰를 계속할수록, 로직을 이해하는 속도가 빨라졌다!

그리고 남의 시선으로 바라본 나의 코드의 문제도 계속해서 떠올랐다..

 

그리고 토론..

아고라 들어가보신 분들은 아시겠지만, 주제랑 의견의 퀄리티가 상당하다..

엄청나게 많은 글이 올라오지는 않지만,

미션 진행하면서 혹은 개발 공부하면서 한 번쯤 고민해봤을 문제를 가지고 의견을 나누고 계신다.

특히 getter의 사용 여부나, 테스트 네이밍 컨벤션에 대해 생각해볼 수 있어서 좋았다.

 

 

 

 

인간적인 배움..

나의 눈물샘을 자극하는 사건이 하나 있었다.

 

피어 리뷰를 나름대로 열심히 남긴 덕에, 많은 분들께서 나의 PR에 찾아와 리뷰를 남겨주셨다.

7분이나 남겨주셨다.. 최고최고

그중 특히 기억나는 리뷰가 있는데..

나를 '고수'라고 칭해주신 어떤 분의 코멘트였다.

 

올해 초, 개발 공부를 처음 시작하고나서

지금까지 단 한 번도 스스로 '잘한다'는 생각을 가져본 적이 없었다.

 

적지 않은 나이에, 나름대로 성취한 것을 다 버리고 새로운 분야에 도전했다.

그만큼 조급했고, 남들과 비교하며 부족한 점에만 집중해왔던 것 같다.

지난 7월에 네이버 블로그에 적었던 글의 일부.

개발 생태계에 입문하고, 계속해서 열등감을 느끼던 나의 모습이 기록되어 있다.

 

근데 저 리뷰 코멘트 한 마디를 보고,

'누군가는 나를 인정해주는구나'라는 생각에 자신감을 많이 얻을 수 있었다.

지나온 날들에 대한 보상을 받는 기분도 들었다.

 

돌이켜보면, 해야 할 일에 집중하기 바빠서 해내온 일들을 흘려보내는 경우가 많았던 것 같다.

가끔은 이렇게 이뤄낸 것도 돌아보며 자신감을 얻을 줄 아는 사람이 되겠다고 다짐했다.

 

그리고 나도 누군가에게 좋은 영향을 줄 수 있도록, 리뷰를 열심히 달겠다고 다짐했다.

이번 주에도 10개의 피어 리뷰를 아주.. 정성스럽게 달아보려 한다..

 

 


이번 주도 무사히.. 행복하다..

 

마지막으로, 이번 한 주도 밀도 높은 성장을 경험하게 해주신 많은 분들께 감사드린다고 말씀드리고 싶다.

아직은 막연하지만, 단단한 철학을 가진 개발자가 된 미래의 모습을 자꾸 그려보게 되는 행복한 한 주였다.

 

한 가지 안타까운 것이..

주차가 거듭될 수록, 지원자들의 PR 수가 떨어지고 있다는 것이다.

 

미션 난이도가 쉽지 않고, 시간 투자도 어느정도 해야하니..

사실 나도 학교, 알바를 병행하며 벅찰 때도 있었지만,

그래도 다른 프로젝트 개발을 미리미리 해둔 덕에, 나름대로 시간과 여유를 확보해둘 수 있었다.

 

하지만 일을 하고 계시거나, 다른 이유로 많이 바쁜 사람들은 미션이 정말 어려울 수도 있겠다는 생각을 했다.

그럼에도, 많은 사람들이 끝까지 함께 하며

많은 것을 함께 배우고 성장하면 좋겠다는 바람이 있다.

왜냐면 내가 성장하는 이 시간이 너무 좋아서.. (맛있는 건 먹어보라고 하고 싶은 것 처럼..)

 

아무쪼록 모두들,

무탈하면서도 크게 성장하는 마지막 주차가 되시길 기원합니다!

 

읽어주신 모든 분들께 감사드립니다.

 

 

 

 

 

 

 

 

 

반응형
반응형

 

 

 

 

예외를 처리하다 보면, 아래와 같은 에러 문구를 쉽게 볼 수 있습니다.

바로 Unhandled exception 입니다.

 

 

 

예외 발생

예외의 종류는 다양하고, 예외가 발생하는 코드도 다양합니다.

본 아티클에서는, 어떤 코드에 의해 예외가 발생하는 상황을

throw new Exception(); 이라는 코드로 대체하겠습니다.

public void firstMethod() {
    System.out.println("First");
    secondMethod();
}

private void secondMethod() {
    System.out.println("Second");
    throw new Exception();
}

위처럼 예외가 발생되었는데, 그것을 처리하는 코드가 없을 때

Unhandled exception 이라는 예외가 발생하고, 컴파일에 실패합니다.

 

 

 

 

예외를 처리

public void firstMethod() {
    secondMethod();
    System.out.println("First");
}

private void secondMethod() {
    try {
        System.out.println("Second");
        throw new Exception();
    } catch (Exception e) {
        System.out.println(e);
    }
}

try catch를 이용해서 Exception을 처리했습니다.

try에서 Exception이 발생하자, 즉시 catch에서 처리됩니다.

그리고 다음 로직으로 이동합니다.

 

 

그런데, 예외가 발생 시 프로그램 동작이 멈춰야할 때가 있습니다.

 

위의 코드에서,

예외가 발생하면 "First"라는 문자열이 출력되지 않아야 한다고 가정해보겠습니다.

 

그럴 때는, thorws를 이용해

예외가 발생하는 메서드(secondMethod)에서

상위 메서드(firstMethod)로 예외를 던지고,

상위에서 처리(catch)하면 됩니다.

public void firstMethod() {
    try {
        secondMethod();
        System.out.println("First");
    } catch (Exception e) {
        System.out.println(e);
    }
}

private void secondMethod() throws Exception {
    try {
        System.out.println("Second");
        throw new Exception();
    } catch (Exception e) {
        throw e;
    }
}

 

 

로직을 자세하게 정리하면 아래와 같습니다.

(풀어써서 길어졌지만 그만큼 간단하니, 이해가 어려우시다면 천천히 읽어보시길 추천드립니다.)

 

1. secondMethod에서 Exception 발생

2. secondMethod 내부에서 Exception 처리(catch)

3. catch 문에서 새로운 Exception 발생

4. throws 키워드 사용하여, secondMethod를 호출한 상위 메서드(firstMethod)로 예외 던지기

5. firstMethod에서 Exception catch

6. System.out.println(e); 로 예외 로그 출력

7. 다음 코드인 System.out.println("First")는 실행되지 않음

 

이렇게 예외가 발생되면 프로그램이 종료되도록 로직을 수정해보았습니다.

(이는 컴파일 후 프로그램 동작 중 발생하는 런타임 에러에 의한 비정상적인 종료와는 다릅니다.)

 

 

 

 

throws하지 않아도 되는 예외? (IllegalArgumentException 등..)

위에서 사용한 Exception은 최상위 예외입니다.

그 아래에는 IllegalArgumentException, IOException 등 다양한 예외가 있습니다.

 

그런데, 일부 예외는 throws를 하지 않아도

Unhandled exception가 발생하지 않습니다.

 

public void firstMethod() {
    try {
        secondMethod();
        System.out.println("First");
    } catch (Exception e) {
        System.out.println(e);
    }
}

private void secondMethod() {
    System.out.println("Second");
    throw new IllegalArgumentException();
}

위 코드에서, secondMethodIllegalArgumentException를 발생시키지만

thorws도 하지 않고, catch도 하지 않습니다.

Exception이었다면 Unhandled exception라는 문구가 발생했을 상황인데요,

 

그 이유는 IllegalArgumentException런타임 예외(또는 Uncheck Exception)이기 때문입니다.

IOException과 같은 컴파일 예외(Check Exception)는 처리가 강제됩니다.

그렇지 않으면 컴파일을 실행할 수 없습니다.

 

하지만 IllegalArgumentException과 같은 런타임 예외는, 처리가 강제되지 않습니다.

프로그램 실행 도중 예외가 발생하면, 프로그램이 비정상 종료됩니다.

 

 

비록 처리가 강제되지는 않지만,

특정 예외가 발생할 수 있다는 것을 설명하기 위해서

throws 키워드를 사용하는 것이 어떨까 하는 것이 개인적인 생각입니다.

 

 

 

 

 

 

 

참고 자료 : https://stackoverflow.com/questions/50640610/how-to-use-throws-illegalargumentexception-in-method-declaration

반응형
반응형

 

헷갈리는 클래스 영역의 변수 용어에 관해 정리해보고자 합니다.

 

 

멤버 변수 : 클래스 영역에 선언된 변수

클래스 변수 : 멤버 변수 중 static이 붙은 변수로, 해당 클래스의 인스턴스 모두가 공유

인스턴스 변수 : 멤버 변수 중 static이 붙지 않은 변수로, 해당 인스턴스만 접근 가능한 변수

 

(메서드 내부의 변수는 지역 변수이고, 자바에는 전역 변수의 개념이 없다고 합니다.

관련 링크 -> https://shm-m.github.io/blog/global_variable)

 

 

 


예제 코드를 통해 살펴보겠습니다.

import java.util.ArrayList;
import java.util.List;

public class Chef {
    private static final String COMPLETE_COOK_ANNOUNCEMENT = " 준비가 완료되었습니다.";
    private static final String COMPLETE_MENUS_ANNOUNCEMENT = "완성된 요리 : ";
    private static List<String> dishes = new ArrayList<>();
    private int countOfMe = 0;

    public void cook(String menu) {
        System.out.println(menu + COMPLETE_COOK_ANNOUNCEMENT);
        dishes.add(menu);
        this.countOfMe += 1;
    }
    
    public void informCountMadeMyMe() {
        System.out.println(this.countOfMe);
    }

    public void informWholeCompleteMenus() {
        System.out.println(COMPLETE_MENUS_ANNOUNCEMENT + dishes);
    }

}

Chef 클래스는 음식을 요리하고,

해당 요리사가 만든 음식 개수와,

총 준비된 메뉴 리스트 알려줍니다.

여기서 변수의 종류를 정리해보면,

 

멤버 변수 : COMPLETE_COOK_ANNOUNCEMENT, COMPLETE_MENUS_ANNOUNCEMENT, dishes, countOfMe

클래스 변수 : COMPLETE_COOK_ANNOUNCEMENT, COMPLETE_MENUS_ANNOUNCEMENT, dishes

인스턴스 변수 : countOfMe

 

와 같습니다.

 

 

아래와 같이 main함수를 실행해보겠습니다.

public class Practice {
    public static void main(String[] args) {
        Chef woojin = new Chef();
        Chef paul = new Chef();

        woojin.cook("밥");
        paul.cook("국");

        woojin.informCountMadeMyMe();
        paul.informCountMadeMyMe();

        woojin.informWholeCompleteMenus();
        paul.informWholeCompleteMenus();
    }
}

 

woojinpaul이 각각 밥과 국을 요리했습니다.

그리고 각자가 만든 요리 개수 묻습니다.

마지막으로 woojin. paul은 전체 준비된 메뉴 리스트를 묻습니다.

 

위와 같이 출력됨을 확인할 수 있습니다.

 

Chef 클래스의 countOfMe 변수(인스턴스 변수)는 각 인스턴스에만 영향을 받았습니다.

 

하지만 woojin이 준비한 밥, paul이 준비한 국 모두 dishes라는 클래스 변수에 추가되었습니다.

dishes 변수는 모든 인스턴수에게 영향을 받았음을 알 수 있습니다.

 

 

멤버 변수와 클래스 변수, 인스턴스 변수의 차이와 활용에 대해 알아보았습니다.

 

 

 

 

 

 

 

 

반응형
반응형

 

 

do while문을 사용하는 방법과, 왜 사용하는지에 대해 정리해보고자 합니다.

 

 

 

while문의 사용

while문은 조건식이 참인 경우 반복을 지속하는 반복문입니다.

while(true) {
	System.out.println("hi");
}
// hi 무한 출력

int count = 0;
while(count < 3) {
	System.out.println(count);
    count += 1;
}
// 0 1 2 출력

 

 

 

 

do-while문의 사용

do {
	System.out.println("hi")
} while(true)
// hi 무한 출력

int count = 0;
do {
	System.out.println(count);
    count += 1;
} while(count < 3)
// 0 1 2 3 출력

위의 while문 코드를 그대로 do while문으로 변경한 코드입니다.

0 1 2 가 출력된 while문이,

do while문에서는 0 1 2 3이 출력됨을 알 수 있습니다.

 

 

그렇다면 do while문을 왜 사용하는 것일까요..

그 이유에 대해 정리해보겠습니다.

 

 

 

 

do-while문을 사용하는 이유

1, 4가 이미 들어있는 numbers라는 리스트가 있습니다.

19 사이의 랜덤 숫자를 뽑아 numbers에 넣고 싶은데,

이미 들어 있는 숫자는 안 된다고 하겠습니다.

 

예제 코드 - while문 사용)

class practice {
    List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 4));
    
    int randomNumber = 0;
    boolean isIn = true;

    while (isIn) {
        randomNumber = Randoms.pickNumberInRange(1, 9);	// 1과 9 사이 숫자를 랜덤 반환
        isIn = number.contain(randomNumber);	// randomNumber가 numbers에 이미 들어 있는지 여부
    }
	numbers.add(randomNumber);
}

while문으로 number에 들어있지 않은 숫자를 뽑을 때까지

randomNumber에 새로운 수를 가져오는 식으로 구현했습니다.

 

그런데, while의 반복이 지속되는 조건(isIn)은 number에 이미 randomNumber가 들어있다는 것입니다.

그런데 randomNumber를 뽑기도 전에 isIntrue라는 것은 코드의 가독성 측면에서 좋지 않습니다.

 

예제 코드의 수정 - do while 적용)

 

class practice {
    List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 4));
    
    int randomNumber = 0;
    boolean isIn;

    do {
        randomNumber = Randoms.pickNumberInRange(1, 9);	// 1과 9 사이 숫자를 랜덤 반환
        isIn = number.contain(randomNumber);	// randomNumber가 numbers에 이미 들어 있는지 여부
    } while(isIn)
	numbers.add(randomNumber);
}

위처럼 do while문을 사용하면,

isIn에 초기값을 설정하지 않아도 되고,

자연스러운 흐름으로 코드를 구현할 수 있습니다.

 

 

이처럼,

"어떤 행동을 최소 1회 수행해야만 -> 반복 여부가 결정"되는 경우에는

do while문을 쓰는 것이 적절합니다.

 

 

 

 

 

 

 

반응형

+ Recent posts