본문 바로가기
부트캠프/우테코 프리코스

1주차의 기록

by 데브겸 2023. 10. 20.

 

드디어 자바 기초 문법 강의를 다 듣고 프리코스 문제에 들어갔다.

1주차의 경우 미션 외에도 개발 환경을 세팅하고 GitHub 저장소를 설정하는 등의 작업이 요구사항으로 주어졌다.

 


개발 환경 셋팅

미션 GitHub 레포지터리를 folk해오고, 이를 내 로컬에 클론, 그 뒤 브랜치를 따서 작업하는 것의 경우 이전에 내가 만들어둔 GitHub 자료를 토대로 빠르게 빠르게 설정했다. 클론 폴더를 Intellij에서 여는 것까지도 스무스~

https://few-geranium-eac.notion.site/Git-Git-Git-GitHub-8d09fb7c4b784656a906db4e34c2b49b?pvs=4

 

실전은 Gitㅔ야, Gitㅔ : Git과 GitHub 찍먹해보기

목차

few-geranium-eac.notion.site

 

문제는 java와 JDK 버전을 맞추는 일이었다. 프로그래밍 요구 사항 중에 JDK 17버전에서 실행 가능해야 한다고 하는데, 이게 무슨 말인지 처음에는 잘 못알아 들었다. java가 언어인거는 아는데 그럼 java 17버전이랑 JDK 17버전이랑 다른 건가...? 각각은 독립 변수인가..?

 

결론적으로 사람들이 Java 버전이라고 하는 것은 JDK 버전을 의미한다고 볼 수 있다. JDK는 자바 개발을 도와주는 도구 모음집이고(개발자들이 자바로 개발하는 데 사용하는 SDK 키트) 그 안에 JRE(Java Class Library, JVM)와 javac 등 컴파일러 등 필요한 것이 다 들어 있기 때문. 참고로 이전 버전에서 컴파일 된 클래스 파일이 상위 버전에서는 호환되지만, 반대로 상위 버전에서 컴파일된 클래스 파일은 아래 버전의 JDK 환경에서는 돌아가지 않는다(이를 Backward Compatibility 문제라고 부르는 것 같았다 - 참고). 아래에서도 그럴 경우 아래에서도 나오는 "Error: A JNI error has occurred, please check your installation and try again" 에러 혹은 "Cause: error :invalid source release"가 나오는듯.

 

여튼 자신의 Intellij 환경세팅 확인을 통해 java17 버전을 선택해서 사용하고 있는지 확인하고, 아니라면 변경하면 된다.

 

설정 및 확인 방법은 맥 기준

 

1. Project Structure 설정

File -> Project Stucture -> Project에서 SDK와 Language level 설정하기

SDK에서 토글을 눌렀을 때 17버전이 없다면 다운로드 받으면 됨. vendor는 유료인 oracle 버전만 아니면 되는 듯

저는 Amazon Corretto를 다운받았고, 애플 실리콘이라 aarch64로 다운받았습니다.

(Language Level을 따로 설정할 수 있는 이유는 JDK와 작성하는 Java 코드의 버전이 다를 경우가 있기 때문) 

File -> Project Stucture -> SDKs에서 17버전이 있는지 확인하기

 

 

2. Project Setting 설정

Intellij IDE -> Setting -> Build Tools -> Gradle

빌드 JVM 버전 변경. 이번에 Gradle을 쓰기 때문에 Gradle에서 변경함

 

 

Intellij IDE -> Setting -> Complier -> Java Compiler

Project bytecode version 변경 

 

 

자바 버전 별로 무엇이 다른지는 아래 포스트들을 참고할 수 있습니다. 아직 내공이 부족해서 그런지 저는 완전히 이해는 안 되네요

https://cheerup313.tistory.com/86

 

[JAVA] 자바 버전에 대하여 (java 8, 11, 17 버전 차이/17버전 사용이유)

나의 경우, 학원에서는 11버전을 쓰고 회사에서는 8 버전을 쓰고 궁금한 건 17버전이다 (?) 자바 버전은 왜이렇게 많으며, 어떤걸 써야 좋은지 모르겠어서 좀 찾아봤다. 자바 JDK LTS버전 https://www.jav

cheerup313.tistory.com

 

https://velog.io/@ililil9482/Java17%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C

 

Java17을 왜 고려해야 할까? (Java version(8~17) 별 특징 정리)

😂 Java11은 왜... Java8과 경쟁상대로 가장 많이 언급되었던 Java version은 11이였다. 그 이유는 우선 LTS였기 때문이였는데. Oracle 사에서 Java8을 사용한 프로젝트들이 너무 많음을 고려하여 Java8이 Java1

velog.io

 

 

 

간혹

Error: A JNI error has occurred, please check your installation and try again 에러가 발생할 수 있는데, 이거는 컴파일 설정된 SDK 버전과 실행하고 있는 자바 버전이 달라서 발생하는 에러임. 나는 분명 앞에서 버전 설정을 잘 해줬는데, 이게 Intellij에서 다른 버전의 JDK로 작성된 프로젝트를 열거나 하면 풀리는 경우가 있는 것 같다. 당황하지 말고 다시 Intellij 설정을 다시 해주자

 

 

 


미션 수행하기

기능 문서 작성하기

개발 환경을 세팅하고 본격적으로 미션에 돌입했다. 이번 미션은 숫자 야구 프로그램을 구현하는 것. 미션지를 천천히 읽어보다보니 요구사항에 '기능을 구현하기 전 docs/README.md에 구현할 기능 목록을 정리해 추가하라고 하였다. 먼저 기능을 구현하는 것이 보통이었는데, 분석과 문서화를 하고 코드를 구현하는 순서가 신기했다. 

 

우선 하라는대로 아래와 같이 문서를 작성했다. 더 신기했던 것은 요구사항에 대해서 생각해보고, 문서를 적는 와중에 자연스럽게 어떤 로직이 필요하고, 어떻게 클래스를 구분하고 등이 조금씩 보이기 시작했던 것. 이래서 먼저 설계 혹은 기능 문서를 적고 구현하라는 것인가 싶었다.

# 미션 - 숫자 야구

## 구현 기능 목록

### 1. IlleagalArgumentException.class
사용자가 잘못된 입력값을 입력했을 때 발생하는 `IllegalArgumentException`에러 구현 
-> 이미 구현되어 있는 에러이므로 우선 구현되어 있는거 사용


### 2. Main.class
1) 컴퓨터에서 난수를 생성
   - Random 값 추출은 `camp.nextstep.edu.missionutils.Randoms`의 `pickNumberInRange()` 활용 
2) 사용자의 입력을 받음
   - 사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()` 활용
3) 사용자의 입력의 유효성검사
4) 유효하다면 난수와 입력 비교
   - 입력 숫자 중 하나가 난수의 숫자 중 하나와 일치한다면
     - 그 중 자리도 일치한다면 '스트라이크' 카운트 + 1
       - '스트라이크' 카운트가 0이면 출력하지 않음
       - 스트라이크의 갯수가 3인 경우
         - 게임을 재시작 할 것인지, 종료할 것인지 사용자의 입력을 받음
           - 재시작한다면 **1) 컴퓨터에서 난수 생성**부터 다시 시작
           - 종료를 한다면 **5) 프로그램 종료로 감**
     - 자리가 일치하지 않는다면 '볼' 카운트 + 1
       - '볼' 카운트가 0이면 출력하지 않음
     - 최종적인 카운트와 볼 카운트 출력
   - 하나도 일치하지 않는다면 '낫싱' 출력
   - **2) 사용자의 입력을 받음**으로 돌아감
5) 프로그램 종료
   - 단, `System.exit()`을 사용하지 않음



### 3. ValidateInput.class
- 사용자가 입력한 값이 올바른 형태인지 검증
  - 입력이 `int`인가?
  - 입력의 길이가 3인가?
  - 입력한 수 세 자리가 모두 다 다른가?



### 4. StrikeOrBall.class
파라미터 두 개를 비교해서
- 입력 숫자 중 하나가 난수의 숫자 중 하나와 일치한다면
  - 그 중 자리도 일치한다면 '스트라이크' 카운트 + 1
- '스트라이크' 카운트가 0이면 출력하지 않도록 함
  - 자리가 일치하지 않는다면 '볼' 카운트 + 1
    - '볼' 카운트가 0이면 출력하지 않음
  - 최종적인 카운트와 볼 카운트 출력
  - 하나도 일치하지 않는다면 '낫싱' 출력

 

 

코드 작성하기

막막함에 눈물이 찔끔,,, 날뻔했으나 우선 돌아가는 쓰레기라도 만들어보자라는 마인드로 가장 쉬운 것부터 구현하기 시작.

 

1. 가장 처음으로 컴퓨터가 난수를 생성하는 것부터 작성하기 시작했다. 그렇게 만든 이후에 나중에 문서를 다시 보다보니 빼먹은 요구사항(중복 숫자가 있으면 안 된다)이 있다는 것을 알고 코드를 다시 작성했다. 꼭... 요구사항을 자세하게 읽자...

 

2. 다음으로는 유저의 입력을 받아서 컴퓨터의 난수와 비교하기 쉽도록 자료형을 맞추는 코드를 작성했다. 이 과정에서 모르는 것들이 대폭등장했는데, Scanner로 받은 input의 자료형이 뭔지부터, 그것을 어떻게 리스트로 만들고, 각각의 원소를 int 혹은 Integer로 만드는지 감이 전혀 안 잡혔었다.

 

 

Scanner로 입력받기

찾아보니 Scanner 객체를 만든 이후에 여러 메소드를 적절히 이용하여 원하는 형태의 자료형으로 리턴하게 하는 패턴인 것 같았다. (이 포스트가 많은 도움이 되었다)

import java.util.Scanner;

public class Main {
	public static void main(String[] args {
    	
        Scanner in = new Scanner(System.in); // Scanner 객체 생성
        
        byte a = in.nextByte();
        int b = in.nextInt();
        boolean c = in.nextBoolean();
        String d = in.next(); // 공백을 기준으로 한 단어를 읽음
        String e = in.nextLine(); // 개행을 기준으로 한 줄을 읽음
    }
}

 

입력의 경우 우테코에서 제공한 라이브러리를 써야 하는데, 뜯어보니 nextLine()으로 구현되어 있어서 String으로 리턴을 받음을 알 수 있었다. 그렇다면 다음 문제는 받은 String형태의 문자 3개를 각각의 숫자 3개로 어떻게 변환할 것인가이다.

 

 

Strings 입력값을 쪼개기

문자열을 쪼개는 것은 우선 찾아보기에 두 가지 방식이 있었다.

 

하나는 String[]을 반환하는 String.split(). ()안에 여러 정규표현식 등을 넣어 문자열을 쪼갤 수 있다. ("")를 입력할 경우 하나씩 쪼개는 것도 가능한듯.

String str1 = "바나나, 사과, 귤";
String[] result1 = str.split(", ");

//["바나나", "사과", "귤"]

String str2 = "123";
String[] result2 = str.split("");

//["1", "2", "3"]

 

다음은 Char[]을 반환하는 String.toCharArray()

String str1 = "123";
String[] result1 = str.split("");

//['1', '2', '3']

 

둘 중에 무엇을 써야 할까 고민이 되긴 했지만, 이후에 나올 Character.isDigit()과 Character.getNumericValue()를 사용하기 위해서 후자를 선택하였다.

 

*참고: 더 찾아보니 String 타입으로 받은 문자열의 인덱스를 하나 받아 char 타입으로 바꿔주는 String.charAt이라는 것도 있는 것 같다. 알고리즘 테스트 등에서 많이 사용하는 것으로 보임

String str = "Hello World";
System.out.println(str.charAt(0));

 

 

String을 Integer 혹은 int로 변환하기

String 타입으로 만든 문자열을 int로 바꾸는 것은 대표적으로 Integer.parseInt()와 Integer.valueOf() 메서드가 있는 것 같았다. 둘 다 전달받은 문자열을 숫자로 반환하지만 Integer.parseInt()는 int형으로, Integer.valueOf()는 Integer 타입으로 반환한다.

String str1 = "100";
int num1 = Integer.parseInt(str1);
Integer num2 = Integer.parseInt(str1);

System.out.println("num: " + num1);
System.out.println("num: " + num2);
// num: 100
// num: 100

 

만약 문자열을 변환할 수 없는 경우 (ex. 숫자와 문자가 합쳐진 문자열이라던가) NumberFormatException 예외가 발생한다. 따라서 try-catch 구문을 활용하는 것을 권장.

 

신기한 것은 둘다 문자열에 대해서도 숫자로 변환해준다는 것이다. 또 두번째 파라미터에 radix(기수 - 숫자 자리 표시법에서 어떤 자리의 가중값... 잘 모르겠음.. 그냥 진수라고 봐도 될듯?) 변환할 것인지를 넣을 수도 있다. 시도해보니까 10, 16, 64에 대해서는 에러가 발생하고 32를 넣어주니까 변환됨)

String str2 = "Java";
int num1 = Integer.parseInt(str2, 32);
Integer num2 = Integer.parseInt(str2, 32);

System.out.println("num: " + num1);
System.out.println("num: " + num2);

// num: 633834
// num: 633834

 

parseInt()와 valueOf()에 대한 좀 더 딥한 얘기는 아래 포스팅에... (나중에 공부해야지)
https://ssdragon.tistory.com/22

 

valueOf() 와 parseInt() 의 차이점

Integer 라는 래퍼클래스(Wrapper Class)가 가지고 있는 메서드들이다. 이 메서드 두 개는 String을 int로 바꿀수 있다. 하지만 차이점이라면 반환값에 있다. Integer.valueOf("10") 은 Integer 타입으로 반환하고,

ssdragon.tistory.com

 

 

 

char을 Integer 혹은 int로 변환하기

하지만 나는 String을 변환하는 parseInt()나 valueOf()를 사용하지 않고, char를 변환해주는 Character.getNumericValue()를 사용했다. Character.getNumericValue()는 char를 int로 변환시켜준다. (물론 char에서 '0' 아스키 코드값을 빼주는 방법도 있긴 한데,,, 이건 좀 직관적이지 않아서 패스했다)

 

사실 Character.getNumericValue()나 Integer.parseInt()는 거기서 거기인 느낌이었는데, 전자를 택한 이유는 Character 클래스에 isDigit()이라는 매력적인 메서드가 있었기 때문이다. isDigit()은 괄호 안에 들어간 char가 숫자인지 여부를 판단하여 boolean 값을 반환해준다. 음수, 소수, 문자 등을 모두 걸러주는 효과가 있는데, 이는 프리코스 미션 중에서 들어가야 하는 로직 중에 하나기 때문에 안 쓸 이유가 없었다. (getNumericValue에서도 int 값이 없으면 -1, 분수나 소수 값이 있으면 -2를 반환해주긴 하지만 isDigit()으로 걸러내는 것이 if 문으로 처리하기가 더 좋았다)

 

최종적으로는 사용자의 입력을 toCharArray()로 char[]로 변환하고, for문을 돌며 각 요소를 isDigit()으로 검사하여 true라면 getNumericValue()로 변환하는 로직을 작성하였다.

for (char c : input.toCharArray()) {
			if (Character.isDigit(c)) {
				candidate.add(Character.getNumericValue(c));
			} else {
				throw new IllegalArgumentException();
			}
		}

 

 


GitHub 원격 저장소에 커밋하기

커밋하는데도 약간의 고생이 있었는데, 로컬에서 커밋을 하고나니 건들면 안 되는 docs를 건드린 것을 알게 되었다. 하지만, 잘못된 커밋은 맨 첫번째 커밋인지라 reset을 하기에는 너무 귀찮은 상황... 어떻게 할까 고민을 하며 구글링을 해보니 git rebase로 특정 커밋을 삭제할 수 있다고 하는 것이다.

 

아래 코드 중 하나를 선택해서 커밋 내역을 확인한다

git rebase -i HEAD~n

git rebase -i {제거하려는 커밋의 직전 커밋 id}

 

그 중에 내가 삭제하고 싶은 커밋의 메시지를 vim 명령어를 통해 pick에서 drop 혹은 d로 바꿔서 :wq! 해주면 된다.

 

역시,, 깃은 실전에서 배우는게 많은듯.