반응형

📘 4주차 미션을 시작하면서

이번 주차 미션은 “다리 건너기” 미션이었습니다. 처음에 문제를 읽었을 때는 한번에 이해가 안 됐었는데, 오징어게임의 다리 건너기 게임과 유사하다는 얘기를 듣고 찾아보니 한번에 이해가 갔습니다.

마지막 미션에서는 기본적인 클래스의 분리는 우테코에서 이미 만들어놓았고, 각 클래스마다 요구조건이 달려있었습니다.

2, 3주차 미션의 모든 요구 조건을 포함하면서 새롭게 추가된 요구조건을 만족하며 작업을 해야 했기 때문에 요구 사항 정리(README.md)만 엄청 오래 걸렸다는…

이번 미션에서는 ‘View와 Domain의 완벽한 분리”, “메소드의 길이는 10줄 이하”에 많은 집중을 하며 기능을 구현했습니다.

 

 

 

📖 BridgeGame 내부에서 View를 사용할 수 없는 요구조건

이번에 새롭게 추가된 요구조건이었습니다. 저는 BridgeGame 클래스를 Domain으로 주어진 클래스라고 판단을 했습니다. 그래서 UI에 사용되는 InputView와 OutputView를 Domain에서 완벽히 분리하기 위한 요구조건이라고 생각했습니다.

Domain과 View를 사용하기 위한 Controlloer를 하나 만들어서 작업했습니다. 요구 조건을 지키면서 작업을 하다보니 메소드의 길이가 10줄이 넘어가는 경우가 정말 많았고, 저번 미션에 주어진 15줄 이하라는 조건은 정말 널널한 조건이었다는 것을 알게되었습니다.

10줄 이하로 메서드를 나누다 보면 정말 작은 단위로 메소드를 자를 수 있게되고, 내가 2주차, 3주차에 작성했던 메소드들이 한 가지 일만 하지 않을 가능성이 높다는 것을 알게되었습니다… 다 끝나고 2주차, 3주차 미션도 한번 다시 풀어보는 시간을 가져야할 거 같습니다.

 

 

 

🙂 4주동안 프리코스를 하며 느낀점

지금까지 많은 자료들을 보면서 공부도 하고 했었지만, 이번 4주가 정말 급성장을 할 수 있었던 기간이었다고 생각됩니다. 만약 프리코스에서 기존처럼 코딩테스트를 진행한 후 붙은 사람들만 프리코스에 참여했었다면 붙지 못했을 거 같습니다.

 

이번 기수부터 프리코스를 확장해서 모두에게 열린 기회를 제공하신거라고 하는데 이번 기수만 3000명이 넘게 참여한 걸로 봐서는 다음 기수는 더 많이 지원할 거 같다는 느낌이 듭니다.

 

그리고 무엇보다도 매주 진행하는 코수타(코치와 수다 타임)가 있는데, 좋은 말도 정말 많이 해주시고 매주 수요일마다 정신 건강을 재정비하는 시간이었다고 생각합니다. 이번에 이것저것 너무 겹치는 일들이 많아서 힘들었는데, 코수타를 통해서 계속 힘내서 했던 거 같아요.

최종 코딩 테스트는 2배수 정도 뽑는다고 들었는데 붙으면 좋겠지만 붙지 못해도 정말 값진 경험했다고 생각합니다. 우테코 프리코스 이전에는 공부 방향을 잡는데 너무 큰 문제를 가지고 있었는데, 프리코스가 끝나고 나니 확실히 달라진 모습을 볼 수 있었습니다.

 

반응형
반응형

📘 3주차 미션을 시작하면서

3주차 미션을 받은 순간... 추가 요구사항이 더욱 디테일해졌다는 것을 느꼈고, 할 수 있겠지라는 걱정부터 시작되기 시작했었다.
자바를 스스로 공부하기 시작하면서 기본적인 자료형, 문법 등에 대해서는 사용도 해보고 했지만 이번 미션에서는 enum(열거형)을 사용하라는 요구사항이 있었다. 2주차 미션을 하면서 '상수'를 적절하게 사용하면 가독성을 높일 수 있구나라는 것을 느꼈고, 다음 미션에는 꼭 사용해봐야지라는 생각을 하고 있었다. 우테코는 정말 신기한게 마치 내가 하고 있는 생각을 알고 있는 것처럼 새로운 미션에 귀신같이 '열거형'을 사용하라는 요구사항이 있었다. 😦

 

 

 

📖 열거형이 무엇인가?

열거형은 자바에 대해서 공부하면서 들어본적은 많았지만 실제로 코드 개발에서 사용해본적은 없다. 그래서 이번에 미션에서 사용하기 위해서는 혼자 학습을 해봐야겠다고 생각했다. '자바의 정석'이라는 책과 구글링을 통해서 열거형에 대해서 학습하고, 블로그에 정리해놓았다. 간단하게 말하면 '열거형'이란 비슷한 역할을 하는 상수들을 모아서 관리할 수 있도록 도와주는 역할을 한다. static final을 사용하지 않고 비슷한 상수는 모아서 사용한다면 훨씬 가독성을 높일 수 있을 거라고 생각했다.

 

 

 

📖 이번 미션에서 힘들었던 점

솔직히 말하면 다 힘들었다. 클래스 분리도 정말 어려웠고, 함수(메소드)의 길이를 15자 이하로 작성하라는 요구 사항을 의식하면서 코드 작업을 했다. 모든 코드를 작성할 때, 자연스럽게 평소보다 많은 생각을 하게되었다. 그리고 처음에 코드를 작성하면서 이건 엄청난 리펙토링 작업이 필요하겠다는 것을 느끼면서 작업했다. 그 중에서 가장 많이 시간을 쏟았던 삽질을 적어보려고 한다. 한풀이 느낌으로 글을 작성할 것이기 때문에 길어질 수 있다는 것을...ㅠ

 

이번 로또 문제 중에 이런 요구 사항이 있었다.

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

예를 들어 처음에 로또 구입 금액을 입력 받는데, 이때 입력 값이 “1000j”라고 해보자.

  • “1000j” 는 ‘j’라는 문자가 포함되어 있기 때문에 금액이라고 할 수 없다.
  • 잘못된 값을 입력했기 때문에 IllegalArgumentException을 발생한다.
try {
        Integer.valueOf("1000j");
} catch (Exception e) {
        System.out.println("[ERROR] 숫자가 아닌 값을 입력했습니다.");
        throw new IllegalArgumentException();
}

처음에는 이렇게 코드를 작성했다.

내가 생각했던 프로그램 진행 방향은 다음과 같다.

  1. “1000j”가 입력 값으로 들어옴.
  2. Interger.valueOf(”1000j”)에서 NumberFormatException이 발생
  3. catch 블럭으로 Exception을 잡아내고, IllegalArgumentException을 발생 시킴.
  4. 프로그램이 종료됨.

생각한대로라면 사용자가 잘못된 값을 입력했고, IllegalArgumentException을 발생했고, “[ERROR]”로 시작하는 에러 메시지도 출력했고, throw new를 통해서 프로그램도 종료되었다.

하지만 ApplicationTest에서 예외_테스트()가 자꾸 실패했다.

@Test
void 예외_테스트() {
    assertSimpleTest(() -> {
        runException("1000j");
        assertThat(output()).contains(ERROR_MESSAGE);
    });
}

왜 실패하지…?;; 이때 부터 삽이 다 닳아서 나무 막대기만 남을 정도로 삽질을 시작했다.

  1. 첫 번째 삽질
    • runException()에 뭔가가 숨어있나보다. 찾아보러 가자.
    • 그렇게 처음으로 woowacourse-projects에 있는 NsTest.java 파일에 가봤다.
    • protected final void runException(final String... args) { try { run(args); } catch (final NoSuchElementException ignore) { } }
    • 코드를 보니 뭔가 저 catch (final NoSuchElementException ignore)이 문제일 거 같다는 생각이 들었다.
    • 그래서 내가 작성한 코드에서 NoSuchElementException을 throw 해보기로 했다.
    • try { Integer.valueOf("1000j"); } catch (Exception e) { System.out.println("[ERROR] 숫자가 아닌 값을 입력했습니다."); throw new NoSuchElementException(); }
    • 이렇게 코드를 작성하고, 다시 ApplicationTest를 돌려봤다. 오…초록불이 들어왔다! 해결한건가?
    • 하지만 문제 요구 조건에서 잘못된 입력이 들어오면 IllegalAegumentException을 발생시키고 라는 요구 조건에 만족하지 못했다.
    • 그래서 혹시나 하는 마음에 미리 만들어놓았던 IllegalArgumentException 발생 여부를 테스트하는 코드를 돌려봤다.
    • @DisplayName("입력된 금액 예외 발생 테스트") @ParameterizedTest @ValueSource(strings = {"3500", "4650", "9080", "10", "100", "1000j"}) void validatePurchaseAmountTest(String money) { assertThatThrownBy(() -> InputException.validatePurchaseAmount(money)) .isInstanceOf(IllegalArgumentException.class); }
    • 역시나 “1000j” 부분에서 빨간불이 들어왔다. ApplicationTest만 통과하면 되는거 아닌가? 라는 생각이 순식간에 지나갔지만 절대…용납할 수 없다. 이런 찝찝한 문제가 발생하면 절대…잠을 잘 수가 없다.
  2. 두 번째 삽질
    • 첫 번째 삽질이 끝나고 ApplicationTest가 통과하려면 일단 NoSuchElementException이 발생되어야 한다는 결론을 무작정 내버렸다.
    • NoSuchElementException 은 발생되어야 하고, IllegalArgumentException도 발생되어야 하고 이게 무슨 혼돈의 카오스인가….
    • “두 개의 예외를 동시에 발생시킬 수 있는가?” 부터 시작해서 정말 모든 키워드는 다 넣어서 구글링을 해봤다.
    • 하지만 어떠한 방법도 ApplicationTest와 내가 만든 테스트가 동시에 통과하는 방법도 없었다.
  3. 결론에 도달
    • 검정색 화면에 있는 코드만 계속 보고 있으니 점점 눈이 흐려질 때쯤 문제를 다시 한번 읽어봐야겠다라는 생각이 들어서 README.md 파일을 다시 천천히 읽어봤다.
    • 잘못된 입력 → IllegalArgumentException 발생 → [ERROR] 출력 → 프로그램 종료….?
    • 갑자기 문득 main 함수가 종료돼도 프로그램이 종료되는 거 아닌가…? 라는 생각이 들었고, 코드를 작성해봤다. 
    • // 프로그램 실행 코드 public void playLottoProgram() { try { savePurchaseAmount(); saveLottoIssueCount(); issueLottoSeveralTimes(lottoIssueCount, myLotto); OutputView.printLottoPurchaseInformation(myLotto, lottoIssueCount); saveWinningNumber(); saveBonusNumber(); setResult(myLotto, winningNumber, bonusNumber); Calculator calculator = new Calculator(); calculator.calculateTotalEarnings(result); calculator.calculateEarningsRate(purchaseAmount); OutputView.printLottoResult(result, calculator.getEarningsRate()); } catch (IllegalArgumentException exception) {} }
    • // 예외 처리 코드 try { Integer.valueOf("1000j"); } catch (Exception e) { System.out.println("[ERROR] 숫자가 아닌 값을 입력했습니다."); throw new IllegalArgumentException(); }
    • 프로그램 실행 코드 전체를 try-catch로 감싸서 IllegalArgumentException이 발생되면 프로그램이 실행이 멈추도록 해봤다.
    • 와…우…. ApplicationTest와 내가 만든 테스트 코드에서 모두 초록색 불이 들어왔다.
    • 정말 간단했던 해결방법인데, 거의 6시간 정도를 빙빙 돌았다. 허무하면서도 이제 잘 수 있다는 안도감에 너무 좋았다. (지금 시간은 오전 2시 24분)
    • 결론은 문제를 잘못 이해했고, woowacourse-projects를 의심했던 벌을 받았다고 생각한다. 틀려도 내가 틀렸지 우테코가 틀렸을리가…없지 역시 😂

 

 

 

📖 다음 미션을 시작하기 전

다음 미션을 받기 전에 이번 미션 회고록을 작성하면서 공통 피드백을 보며 필요한 부분은 학습하도록 한다. 이번 공통 피드백 내용은 다음과 같습니다. (필요한 부분만 가져왔습니다.)

 

💡 비즈니스 로직과 UI로직을 철저히 분리해라.

비즈니스 로직과 UI 로직이 한 개의 클래스에 있는 것은 단일 책임(SRP)에 위배된다.

View에서 사용해야되는 데이터라면 getter를 만들어서 전달하고, 현재 객체의 상태를 로그에 나타내기 위함이 강하다면 toString()을 사용해서 정의한다.

 

💡 연관있는 상수의 경우는 static final 보다는 enum을 사용한다.

 

💡 final 키워드를 사용해서 값의 변경을 막는다.

최근에 등장하는 프로그래밍 언어들의 기본은 ‘불변’이다. 자바에서는 불변을 위해서 final 키워드를 사용할 수 있다.

final은 상수 선언할 때만 사용하는 줄 알고 있었다. 필드(인스턴스 변수)의 불변을 위해서도 사용할 수 있다는 것을 알아두자.

 

💡 객체의 상태 접근을 제한한다.

인스턴스 변수의 접근 제어자는 private를 사용한다. 객체의 캡슐화를 하기 위한 기본적인 설정.

 

💡 객체는 객체스럽게 사용한다.

객체 내부에 로직을 구현한다. 일반적으로 getter()를 많이 사용하는 경향이 있다. 하지만 객체의 모든 인스턴스 변수를 보내줘야 하는데, 이 모든걸 getter()로 구현한다면 과연 가독성이 좋아질까?

그렇기 때문에 View에서 단순 출력을 하기 위한 데이터 정도는 getter()로 전해줄 수 있지만, 그 이외의 경우에는 getter로 데이터를 보내서 로직을 처리하기 보다는 데이터를 객체로 받아와서 로직을 돌린 후 결과를 날려주는 형식으로 구현하는 것이 좋다.

getter를 사용하는 대신 객체에 메시지를 보내자

 

💡 필드(인스턴스 변수)의 수를 줄이기 위해 노력한다.

객체에 인스턴스 변수가 많을 수록 객체의 복잡도를 높이고, 버그 발생의 가능성을 높일 수 있다. 필드에 중복이 있거나, 불필요한 필드가 있지는 않은지 확인해볼 필요가 있다.

 

💡 성공하는 테스트 뿐만 아니라 예외에 대한 케이스도 테스트 해야 한다.

 

💡 테스트 코드도 코드이다.

즉, 테스트 코드도 중복되는 부분이 존재한다면 줄이는 것이 바람직하다. 예를 들어 단순히 값이 바뀌는 경우라면 @ParameterizedTest를 사용해서 테스트할 수 있다.

 

💡 테스트를 위한 코드는 구현 코드에서 분리되어야 한다.

테스트를 통과하기 위해 구현 코드에 테스트를 위한 코드를 작성해서는 안된다.

  1. 테스트를 위해서 접근 제어자를 바꾸는 경우
  2. 테스트 코드에서만 사용되는 메서드

 

💡 단위 테스트하기 어려운 코드를 단위 테스트 하기

메서드 시그니처를 수정하여 테스트하기 좋은 메서드로 만들기

 

💡 private 함수를 테스트하고 싶다면 클래스(객체) 분리를 고려한다.

가독성을 높이기 위해서 private 함수를 구현한 경우 public으로도 검증 가능하다고 여겨질 수가 있다. 왜냐하면 public 함수가 private 함수를 사용하기 있기 때문에 자연스럽게 테스트 범위에 포함이 된다.

하지만 가독성 이상의 역할을 하는 경우에는 테스트를 쉽게 구현하기 위해서 해당 역할을 수행하는 다른 객체를 만들 타이밍이 아닌지 고민해봐야한다.

다음 단계를 진행할 때는 너무 많은 역할을 하고 있는 함수나 객체를 어떻게 의미 있는 단위로 분할할지에 초점을 맞춰 진행한다.

 

 

 

🔥 다음 미션을 시작하기 전 마음가짐

아직 문제를 자세하게 읽어보진 않았지만, 슬랙 채널에서 다른 분들이 말하는 걸 보면 오징어 게임에 나오는 '다리 건너기' 게임을 구현하는 미션인 것 같다. 최근에 정말 많은 일들이 한번에 몰려와서 체력적으로 딸리는 것이 느껴진다... 그래도 지금까지 해온 것이 있으니깐 포기하지 않고 마무리 잘하고 싶다.

 

반응형
반응형

2주차 미션의 추가 요구 사항

2주차 미션이 마무리되었다. 온보딩 미션에서와는 다르게 추가적인 요구사항들이 존재했던 미션이었습니다.

추가적인 요구사항은 다음과 같습니다.

  1. indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
  2. 3항 연산자를 쓰지 않는다.
  3. 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
  4. JUnit 5와 AssertJ를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인한다.
  5. 자바 코드 컨벤션을 참고하여 코드를 작성
  6. 커밋 메시지 컨벤션을 참고하여 깃허브 커밋 메시지 작성

 

 

 

구현 기능 목록 정리

이때부터 확실히 뭔가 배워간다는 것을 느꼈습니다. 1주차 때는 기능을 구현하면서 기능목록을 명세했었습니다. 하지만 이번 2주차 미션 때는 구현하고자 하는 기능들을 먼저 작성했습니다.

주로 저는 노션을 자주 사용하고, 제가 정리한 기능 목록도 노션에 먼저 기록해서 README.md 파일에 저장해서 커밋을 했습니다.

기능 구현을 시작하기 전에 기능 목록을 정리를 해보니 어떤 순서대로 구현을 해야겠고, 얼마나 작은 기능까지 나눌 수 있을까라는 고민을 많이 하게되었습니다. 그리고 이렇게 정리해둔 기능 목록에 따라서 기능 구현을 하다보니 내가 구현해야되는 기능에 대해서 더 확실히 알고 있는 상태에서 구현할 수 있었고, 개발 속도도 더 빠르게 진행되었다고 체감으로 느꼈습니다.

 

 

 

기능을 구현하면서 느꼈던 점

이번 2주차 미션에서는 자바 컨벤션 코드도 지키면서 메소드를 최대한 작게 나누어 기능을 구현하는 것에 초점을 맞추고 기능 구현을 시작했습니다.

 

메소드의 기능을 최소한(1개)으로 나누고 작업을 시작하니 신기하게 메소드 이름을 작명할 때도 이전보다 더 쉽게 작명할 수 있다는 것을 느꼈습니다. 메소드 이름은 동사로 시작하게 짓는 것이 컨벤션 내용에 있었지만, 지금까지 구현하던 메소드들은 기능이 2개 이상이었기 때문에 동사를 2개 써야돼서 메소드 이름 작명에 어려움을 느꼈던 거 같습니다. 그래서 기능을 최소한으로 나누고 보니 동사 1개만 사용해서 이름을 작명할 수 있었습니다. 하지만 아직 아쉽다고 느껴지는 것이 메소드의 기능을 표현할 적절한 동사를 찾는 것이 어려웠고, 동사 뒤에 붙는 이름을 작명하는 것이 어려웠습니다.

 

이렇게 메소드 이름을 신경쓰면서 작업함으로써 얻은 효과는 주석을 많이 줄일 수 있었습니다. 이전 미션에서는 기능을 가지고 있는 메소드 뒤에는 무조건 주석을 작성했었습니다. 이때까지만 해도 주석으로 설명을 추가해주는 것이 좋은 코드를 작성하는 방법이라고 생각했었습니다. 어떤 분의 글을 제가 읽었는데 최고의 주석은 잘 만들어진 메소드 이름이라는 글을 봤었습니다. 메소드 이름을 잘 만들어주면 쓸데없는 주석을 다 지워도 무방하다는 것을 의미합니다. 그래서 2주차 미션에서 가장 시간을 많이 썼던 것이 “메소드는 하나의 기능만을 하게 만들어라”“메소드의 이름만 봐도 어떤 기능인지 알게 만들어라” 였습니다.

 

두 번째는 이번에 추가된 요구사항 중 Test에 관련된 내용이었습니다. 단위 테스트가 중요한 이유에 대해서 찾아보고 Junit5에 관련된 내용을 공부했던 거 같습니다. 저는 이번에 리펙토링 과정에서 코드 변경이 많았었습니다. 하지만 신기하게도 코드 리펙토링을 하더라도 그 메소드의 기능은 바뀌지 않습니다. 그렇기 때문에 제대로된 테스트 코드를 작성해뒀다면 리펙토링을 한 코드도 같은 기능에 대해서 빠르게 테스트해볼 수 있었습니다. 이게 아마도 단위테스트의 맛이지 않을까 싶습니다. 초록색 글자는 언제 봐도.. 짜릿합니다.

 

 

 

 

2주차 미션을 마무리하며

이번 미션을 진행하면서 얻었던 내용을 다시 정리하면 다음과 같습니다.

  1. 기능 목록을 먼저 작성해라.
    • 오히려 개발 속도를 증가시킬 수 있다.
    • 내가 구현하고 있는 구현을 정확하게 알고 구현할 수 있다.
  2. 메소드를 최소한의 기능단위로 나누어라.
    • 메소드 이름 작명을 하기 편해진다.
    • 단위 테스트를 할 때, 메소드별로 적용시킬 수 있다.
  3. 메소드 이름과 변수 이름 작명에 힘을 쓰고, 쓸데없는 주석은 지워라.
    • 난잡한 주석을 지울 수 있고, 코드의 가독성을 높여준다.
  4. 정확한 단위 테스트 코드를 하나 만들어둬라.
    • 코드 리펙토링 후 테스트에 드는 비용(시간)을 최소화할 수 있다.

 

이 4가지에 대해서는 이제 알고 학습하기 시작했고, 앞으로 코드 개발에서 꾸준히 신경써야하는 요소들이라고 생각합니다. 다음 3주차 미션에서도 꾸준히 적용되어야 하는 사항이라고 생각하고, 프리코스가 끝나는 시점에서는 모든 요구사항들을 적용시켜 기능 구현을 할 수 있는 능력을 키우고 싶습니다. 남은 2주도 화이팅.

 

반응형
반응형

우아한 테크코스 5기

졸업을 앞두고 계속해서 프로그래밍 공부를 학습하고 싶다는 생각이 들었고, 이왕이면 열정이 확인된 사람들과 함께 해보고 싶다는 생각을 해서 우아한 테크코스 5기에 지원하게 되었습니다.

 

이번 우아한 테크코스 5기는 지난 기수와는 다르게 선발 과정에 변화가 있었습니다. 이전에는 코딩 테스트를 진행한 후 프리코스를 진행할 수 있었지만, 이번 기수는 서류만 제출했다면 모두가 프리코스를 진행해 볼 수 있다는 특이점이 있었습니다.

 

많은 후기 자료들을 봤었는데 프리코스에서도 배우는 것이 많다고 해서 경험해 보고 싶은 마음에 바로 지원해서 지금은 프리코스를 진행 중입니다.

 

 

🤔 회고를 작성하기로 한 이유

이번 기수에서부터 우아한 테크코스 커뮤니티인 “Github Discussions”를 진행하고 있고, 프리코스 참여자들이 서로 학습 콘텐츠를 공유하고 어떤 주제에 대해서 토론도 할 수 있으며, 피어 코드 리뷰도 진행합니다. 그중에서 회고 내용을 공유할 수 있는 카테고리가 있어서 뭐든 기록해 놓으면 좋을 거 같아서 프리코스에 대한 회고를 작성하기로 마음먹었습니다.

 

 

1주 차 미션을 진행하면서 느낀 점

1주 차 미션은 자바 온보딩 미션으로 JDK를 설치하고, 기본적인 Git과 Github에 대한 사용, 개발을 할 수 있는 IDEA인 IntelliJ에 대한 설정으로 기본적인 개발 환경에 익숙해지는 기간이라고 생각했습니다.

 

개인적으로 1주 차에서 가장 배움이 많은 부분이라고 하면 Github를 사용하는 것이라고 생각됩니다. 이전에 Github를 사용할 때는 수정한 부분에 대해서만 간략하게 메시지를 작성했었습니다. 하지만 이번 프리코스에서는 Github 메시지를 남기는 방법에 대한 컨벤션자료를 참고해서 진행해 봤습니다. 확실히 작업한 내용의 type 별로 구분을 해서 커밋 메시지를 작성해 보니 히스토리를 보면 한눈에 원하는 부분의 코드를 찾아서 볼 수 있다는 것을 느꼈습니다.

 

1주차 미션으로 제공되었던 문제가 7개 있었는데 알고리즘 문제와 유사했습니다. 다만 다른 점이 있었다면 여러 가지 예외에 대한 테스트 케이스가 제공되지 않았고 제한 사항도 구체적으로 제시되어 있지 않았습니다. 이 부분에 대해서는 11월 2일에 진행했던 ‘코수타’에서 취지가 있다고 하셨습니다. 취지는 다음과 같습니다.

실세계(우리가 살고 있는 세계)에서는 구체적인 제한 사항이 주어지는 경우가 그렇게 많지 않다. 문제를 해결하며 추가로 발생할 수 있는 제한 사항과 예외에 대해서 스스로 생각해 볼 수 있는 힘을 기르는 것이 중요하다.

개인적으로 이 취지는 정말 괜찮은 취지라고 생각한다. 프리코스에서 배울 점이 많다고 한 것이 확 느껴지기 시작했습니다. 다음 2주 차 미션부터는 해당 취지를 생각하면서 여러 가지 발생할 수 있는 예외 사항을 스스로 생각해 보면서 코드를 작성해 봐야겠다는 생각을 하게 되었습니다.

 

 

1주 차 미션에서 아쉬웠던 점

코드가 실행되기만을 기대하면서 코드를 작성했던 것이 가장 아쉬웠다고 생각합니다. 1주 차가 끝나고 나서 보니 생각하지 못했던 예외 사항이 있었다는 것도 발견하게 되었습니다.

 

자바 코드 컨벤션을 지키지 못한 부분도 있었던 거 같습니다. 1주 차에서는 별다른 코드와 깃허브에 대한 요구사항이 없었지만 나름 적용하면서 코드를 작성하고 싶었는데 아쉬움이 남습니다.

 

2주 차 미션에서는 자바 코드 컨벤션을 보면서 변수와 메서드명 네이밍에 조금 더 힘을 써볼 생각이고, 사소한 공백에도 신경 쓰면서 코딩해 봐야겠다는 생각을 가지고 있습니다!

반응형
반응형

스스로 공부하고 개인적으로 기록해보는 공간입니다. 틀린 부분이 있다면 알려주시면 바로 수정하겠습니다. 감사합니다. 😄

📘 운영체제란 무엇인가?

📖 운영체제(Operating System)가 무엇인가?

컴퓨터를 이용하는 사용자가 컴퓨터 하드웨어와 상호작용할 수 있도록 도와주는 소프트웨어를 말한다. 사용자가 직접 하드웨어에 접근하여 정보를 처리하는 것은 어렵기 때문에 그 역할을 운영체제가 도와준다.

하나의 예시를 들어보면 컴퓨터는 0과 1로 데이터를 표현하는데, 만약 운영체제가 없다면 사용자는 직접 0과 1로 구성된 데이터를 이용하여 직접 컴퓨터와 상호작용해야하는 어려움이 있다.

대표적인 운영체제로는 Windows, MacOS, Linux, IOS 등이 있다.

image

📖 Bootstrap Program이 무엇인가?

컴퓨터나 노트북을 처음에 켜면 windows 아이콘이나 mac 아이콘이 뜨면서 뭘 로딩하는 화면을 볼 수 있다. 이처럼 하드웨어를 사용하기 위해서 전원을 켜면 운영체제를 실행시켜줘야하는데 이 역할을 해주는 것이 바로 Bootstrap Program이다.

즉, Bootstap Program은 전원을 켰을 때, 가장 먼저 작동하는 프로그램을 말하고 이 프로그램은 RAM이 아닌 ROM에 저장되어 있습니다. 전원이 켜지면 CPU는 ROM에 저장된 Bootstrap Program을 읽고, Disk로 부터 OS(운영체제)를 Load하고 실행시키는 역할을 합니다.

🤔 왜 Bootstrap Program은 ROM에 저장되어있을까?
ROM은 Read Only Memory로서 비휘발성 메모리이다. 즉, 전원이 꺼지면 정보가 날라가는 RAM과는 다르게 전원이 꺼져도 정보가 날라가지 않는 특징이 있기 때문에 Bootstrap Program은 ROM에 저장이 된다.

image

📖 사용자가 운영체제를 통해서 하드웨어와 어떻게 상호작용하는가?

그럼 운영체제를 실행시키고 사용자는 어떻게 운영체제와 상호작용하여 하드웨어를 제어할 수 있을까라는 의문이 든다. 운영체제는 크게 Shell(명령어 해석기)과 Kernel(커널)로 나눌 수 있습니다. 아래 그림과 같은 방법으로 사용자가 운영체제를 통해서 하드웨어와 상호작용할 수 있습니다.

사용자는 Shell(ex. bash)을 통해서 명령어를 치면 명령어를 해석해서 Kernel에 요청을 보냅니다. 그럼 Kernel에서는 요청받은 로직(일)을 처리하고 결과를 출력합니다.

주로 사용자들은 CLI(Command Line Interface)나 GUI(Graphical User Interface)를 통해서 운영체제에 명령을 요청합니다.

image

📖 커널(kernel)은 무엇일까?

Shell을 사용해서 우리는 명령어를 치고 그에 대한 결과를 받을 수 있었다. 그럼 커널(Kernel)은 무엇일까?

커널은 프로세스, 메모리, 저장장치를 관리하는 핵심적인 기능을 합니다. 즉 운영체제의 가장 핵심적인 부분이라고 할 수 있습니다. 이러한 커널에는 System Call드라이버라는 것이 존재합니다.

 

  1. System Call (시스템 호출)image
    우리가 응용 프로그램을 사용하는 모든 부분은 user mode일 때 실행됩니다. 커널의 경우 Hardward을 직접적으로 제어할 수 있기 때문에 보호장치라고 할 수 있는 System Call이 존재합니다. System Call의 경우 write(), read(), printf()와 같은 메서드를 이용하여 사용할 수 있습니다. user mode에서 이런 System Call이 들어온다면 Kernel mode로 들어가 Hardware를 제어할 수 있습니다.

 

  1. 드라이버
    Kernel과 Hardware 사이의 인터페이스를 드라이버라고 합니다. 커널의 입출력의 기본적인 부분만 제작되어 있어서 마우스, 키보드와 같은 hardware의 경우 노트북이나 컴퓨터에 꽂기만 해도 작동이 되는 것을 볼 수 있습니다. 하지만 프린터와 같은 장치들은 사용할 때 해당 제작사에서 제공하는 드라이버를 다운로드 받아서 사용했던 경험을 해본적이 있으실 겁니다. 이때 사용하는 것을 디바이스 드라이버라고 합니다.

🔎 Reference

[운영체제(OS)] 1. 운영체제란?
[운영체제(OS)] 운영체제란 무엇인가?

반응형
반응형

📘 스택과 큐 (Stack / Queue)

📖 Stack (스택)

스택 자료구조는 마지막에 들어간 데이터가 제일 먼저 나오는 LIFO(Last In First Out) 구조입니다. 자바에서는 스택을 Stack class로 구현하여 제공하고 있어서 아래와 같이 사용할 수 있습니다.

Stack<Integer> stack = new Stack<>();

스택 활용의 간단한 예시로는 홈페이지의 뒤로가기, 앞으로가기 버튼의 기능과 같습니다.

초기 상태 (현재 있는 페이지는 "구글")
BackStack ["네이버", "다음", "구글"]
ForwardStack []

뒤로가기 버튼을 눌렀을 때 (현재 있는 페이지는 "다음")
BackStack ["네이버", "다음"]
ForwardStack ["구글"]

뒤로가기 버튼을 다시 한번 눌렀을 때 (현재 있는 페이지는 "네이버")
BackStack ["네이버"]
ForwardStack ["구글", "다음"]

📖 Stack Method

  1. empty()
    Stack이 비어있는 상태인지를 알려줍니다. 비어있다면 true를 반환합니다. 주로 while문을 사용하여 스택을 다룰 때 사용합니다.
  2. peek()
    Stack의 가장 마지막에 있는 객체를 반환합니다. peek()는 객체를 보여주기만 할 뿐 스택에서 제거하지 않습니다. 만약 Stack이 비어있다면 EmptyStackException을 발생시킵니다.
  3. pop()
    peek()와 유사하게 가장 마지막에 있는 객체를 반환합니다. 하지만 pop()의 경우 꺼내서 보여준 객체를 Stack에서 제거합니다.
  4. push(Object object)
    Stack에 객체를 저장합니다.
  5. search(Object object)
    Stackd에서 객체 object를 찾아서 위치(int)를 반환합니다. 배열과는 다르게 위치는 0이 아닌 1부터 시작합니다. 만약 찾고자하는 객체가 없다면 -1을 반환합니다.

📖 Queue(큐)

큐 자료구조는 처음에 들어간 데이터가 가장 먼저 나오는 FIFO(First In First Out) 형태의 자료구조 입니다. 자바에서는 Queue를 인터페이스로 정의만 해놓았기 때문에 Queue 인터페이스를 구현한 구현체들 중에서 하나를 골라서 사용해야 합니다.

자주 사용되는 구현체로는 LinkedList와 PriorityQueue가 있습니다. 사용법은 다음과 같습니다.

Queue<Integer> queue1 = new LinkedList<>();
Queue<Integer> queue2 = new PriorityQueue<>();

📖 Queue Method

  1. add(Object object)
    객체를 Queue에 추가합니다. 추가하는데 성공하면 true를 반환하고, 저장공간이 부족할 경우에는 illegalStateException을 발생시킵니다.
  2. peek()
    스택에서와 비슷하게 Queue에서 객체를 읽어서 반환합니다. 이때 Queue에서 객체를 삭제하지는 않습니다.
  3. poll()
    peek()와는 다르게 Queue에서 객체를 꺼내서 반환합니다. 이때 Queue에서 꺼낸 객체는 삭제됩니다. 만약 Queue가 비어있다면 null을 반환합니다.
  4. offer(Object object)
    Queue에 객체를 저장합니다. 저장에 성공하면 true, 저장에 실패하면 false를 반환합니다.
  5. remove()
    Queue에서 객체를 꺼내서 반환합니다. 만약 비어있다면 NoSuchElementException을 발생시킵니다.
  6. element()
    peek()와 기능이 동일합니다. 하지만 만약 Queue가 비어있을 경우에는 NoSuchElementException을 발생시킵니다.

📖 PriorityQueue

Queue 인터페이스의 구현체 중 하나입니다. 저장하는 순서에 상관없이 우선순위가 높은 것부터 꺼내는 특징을 가지고 있습니다. null은 저장할 수 없습니다.

PriorityQueue는 가장 큰 값이나 가장 작은 값을 빠르게 찾을 수 있다는 특징을 가지고 있습니다.

예를 들어 보면 데이터를 3, 4, 1, 7, 8 순서대로 저장했을 때, 우선순위로는 숫자가 가장 작은 1이 제일 먼저 나옵니다. 데이터를 하나씩 뽑아서 확인했을 때 1 → 3 → 4 → 7 → 8 순서대로 나오는 것을 확인할 수 있습니다.

📖 Deque

Queue와 Stack을 합쳐놓은 형태와 유사한 자료구조이다. 즉, 양쪽에서 추가/삭제가 가능하다.

  1. offerLast()
    Queue의 offer(), Stack의 push()와 같은 기능을 가집니다. 객체를 Deque에 저장합니다.
  2. pollLast()
    마지막 데이터를 꺼내서 가져오기 때문에 Stack의 pop()과 같은 기능을 가집니다.
  3. pollFirst()
    첫 번째로 들어간 데이터를 꺼내서 가져오기 때문에 Queue의 poll()과 같은 기능을 가집니다.
  4. peekFirst()
    첫 번째로 들어간 데이터를 반환합니다. 데이터는 사라지지 않기때문에 Queue의 peek()와 같은 기능을 가집니다.
  5. peekLast()
    마지막으로 들어간 데이터를 반환합니다. 데이터는 사라지지 않기 때문에 Stack의 peek()와 같은 기능을 가집니다.

스택과 큐 관련 문제 풀어보기 - 프로그래머스

반응형

+ Recent posts