반응형

자바 프로젝트를 실행할 때는 컴파일이 완료된 프로젝트 파일의 압축형태의 실행파일 .jar파일로 배포를 진행한다.

단일 파일 컴파일

코드로 작성한 자바파일을 실행시키기 위해선 컴파일 과정을 거쳐야한다.

이 때 자바 단일 파일은 컴파일 이후 즉시 실행시킬 수 있다.

javac HelloWorld.java

------ 컴파일 완료(Helloworld.class파일 생성) ------

java HelloWorld

위와 같은 명령어를 통해 실행이 가능

멀티 파일 컴파일

javac -d out src/com/example/*.java

--- 외부 라이브러리(jar) 참조
javac -d out -cp "libs/*" src/com/example/*.java

srt/com/example/ 내의 전체 파일을 탐색하여 컴파일

jar 파일 생성

jar cfe HelloWorld.jar HelloWorld HelloWorld.class

c : 새로운 JAR파일 생성
f : 출력 JAR 파일 이름을 지정
e : 실행 파일의 진입점(메인 클래스)

java -jar HelloWorld.jar

위와 같이 실제 컴파일 이후 실행(배포)까지의 과정이 상당히 번거롭다

때문에 이후 여러 컴파일, 빌드 도구가 나왔는데, 대표적으로 Maven, Gradle이 있다.


빌드 도구

Gradle은 빌드 도구라고 불리는데 다음과 같은 작업을 돕는다.

  • 컴파일
    • 개발된 프로젝트는 하나의 파일로 실행되지 않기 때문에 여러 파일을 모두 컴파일, 외부 라이브러리 등록+컴파일 등 여러 작업을 한번에 진행해야 한다.
      때문에 명령어로 관리하기 힘들어졌고, 빌드 도구가 나오게 되었다.
  • 의존성 관리
    • 프로젝트에서 필요한 라이브러리, 모듈등을 등록 후 간편하게 사용할 수 있다.(build.gradle 파일 의존성 설정)
  • 패키징
    • 프로젝트의 전체 파일을 컴파일 및 외부 라이브러리를 등록 후 패키징 작업을 진행한다.
      패키징 파일은 .jar or .war로 패키징 되고, 이 패키징 된 파일을 실행하면 배포가 가능하다.
  • 테스트 자동화
    • 별도로 작성된 테스트 코드를 실행하여 프로젝트의 런타임 환경의 실 테스트를 진행한다.
  • 빌드 파이프라인(CI/CD) 통합

.jar 파일이란

myFile.jar
│
├── META-INF/
│   └── MANIFEST.MF  # 메타데이터와 JAR 파일의 정보가 포함된 파일
│
├── com/
│   └── example/
│       ├── MyClass.class  # 패키지에 속한 컴파일된 클래스 파일들
│       └── AnotherClass.class
│
├── resources/
│   ├── config.properties  # 리소스 파일
│   └── image.png  # 이미지 파일
│
└── lib/
    ├── library1.jar  # 포함된 라이브러리 파일들
    └── library2.jar


+ 스프링 부트 사용시
│
└──BOOT-INF/
      ├── layers.idx  # 스프링 부트 메타정보
    └── classpath.idx

.jar파일은 프로젝트를 실행하기 위한 최종 단계인데, 아래와 같은 명령어를 통해 생성한 프로젝트 파일을 실행할 수 있다.

java -jar myFile.jar
  • jar파일은 자바 프로젝트를 실행할 수 있도록 만들어진 zip파일 형식의 압축 파일이다.
  • 클래스 파일의 묶음 jar파일은 실행할 수 있는 상태이기에 기본적으로 컴파일 작업이 완료된 .class 파일로 변환되어 있다.
  • jar파일에는 메타데이터가 포함된 META-INF/MANIFEST.MF 파일이 있다. 이 파일에는 jar파일의 메타정보가 들어있다(버전 정보, 메인클래스 등)
    • 메인 클래스를 반드시 설정해야 이후 jar파일이 실행 가능하다.
    • 메인 클래스를 설정하지 않는 경우도 있는데 이 경우는 외부 라이브러리 배포용도로 사용할 때는 실행할 필요가 없기에 설정하지 않는다.

BOOT-INF

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: org.itech.techsignapi.TechsignApiApplication
Spring-Boot-Version: 2.7.18
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx

아래는 스프링 부트를 사용하면 그래들 빌드 시 같이 넘어오는 정보

BOOT-INF/layers.idx

- "dependencies":
  - "BOOT-INF/lib/"
- "spring-boot-loader":
  - "org/"
- "snapshot-dependencies":
- "application":
  - "BOOT-INF/classes/"
  - "BOOT-INF/classpath.idx"
  - "BOOT-INF/layers.idx"
  - "META-INF/"
  • 스프링 부트 애플리케이션의 레이어드 JAR 구조를 정의하는 파일로, 각 파일이 어떤 레이어에 속하는지를 기록

BOOT-INF/classpath.idx

- "BOOT-INF/lib/spring-cloud-starter-openfeign-3.1.7.jar"
- "BOOT-INF/lib/spring-cloud-openfeign-core-3.1.7.jar"
- "BOOT-INF/lib/spring-cloud-starter-3.1.6.jar"
- "BOOT-INF/lib/jjwt-0.9.1.jar"
- "BOOT-INF/lib/jaxb-api-2.3.1.jar"
- "BOOT-INF/lib/springdoc-openapi-ui-1.6.15.jar"
- "BOOT-INF/lib/gson-2.8.7.jar"
- "BOOT-INF/lib/querydsl-jpa-5.0.0.jar"
- "BOOT-INF/lib/querydsl-apt-5.0.0.jar"
- "BOOT-INF/lib/s3-2.20.42.jar"
- "BOOT-INF/lib/aws-java-sdk-s3-1.12.281.jar"
- "BOOT-INF/lib/ec2-2.16.47.jar"
- "BOOT-INF/lib/aws-xml-protocol-2.20.42.jar"
- "BOOT-INF/lib/aws-query-protocol-2.20.42.jar"
- "BOOT-INF/lib/protocol-core-2.20.42.jar"
- "BOOT-INF/lib/aws-core-2.20.42.jar"
- "BOOT-INF/lib/auth-2.20.42.jar"
- "BOOT-INF/lib/regions-2.20.42.jar"
...
  • 스프링 부트 애플리케이션의 클래스패스 정보를 기록하여, 애플리케이션이 실행될 때 필요한 리소스와 클래스를 정확히 로드할 수 있도록 돕는다.
반응형
반응형

https://github.com/woowacourse-precourse/java-baseball-6/pull/1863

 

[숫자 야구 게임] 이상수 미션 제출합니다. by tkdtn4657 · Pull Request #1863 · woowacourse-precourse/java-baseba

 

github.com

 

첫번째로 README.md 파일을 통해 구현할 기능 목록을 정리하는 부분에서 설계를 어떻게 해야할지 미리 고민해봐야하는데, 해당 내용을 작성 후 설계하는 과정에서 README의 대략적인 흐름만 있을 뿐 정확하게 작성하지 못했다.

다음 주차에서는 README를 작성할 때부터 어떻게 설계해야할지 좀 더 구체적으로 생각하고 설계하는 습관을 길러보아야겠다.

코드를 구현하면서는 두가지 큰 이슈가 있었는데, 첫 구현은 쉬웠지만 이 코드를 리팩토링하려고 보니 어느 부분을 손대야할지 도무지 감이 잡히지 않았고, 블로그의 글도 정리가 잘 되어서 좋긴 하지만 전체적인 구상능력이 부족했던 내게는 블로그로는 완벽히 해낼 수 없다고 판단되었고, 급하게 객체지향의 사실과 오해를 구매 후 읽기 시작했다.

해당 책에서는 설계적 관점에서 객체는 각자의 책임을 갖고있어야 한다고 말을 했고, 이를 RDD(Responsibility Driven Design)방식이라 설명했다. 이 방식은 클래스간의 협력은 서로 메시지만 던져줄 뿐 상대에게 내부로직은 알아서 하도록 맡겨야한다는 부분을 강조했고, 이것이 캡슐화와 큰 연관이 있다고 했다. 때문에 해당 로직을 검증 해보았을 때 Baseball객체는 입력값을 받아서 그것을 변환하는 것이 아닌 변환만 해주는 클래스가 필요하단 것이 생각났고, 이를 통해  리팩토링 과정에서 InputLineConverter클래스 생성 후 입력받은 값을 검증해주는 IllegalCheck클래스에 메시지를 넘겨서 이상없다는 확인을 받고 해당 값을 컨버트하는 등 서로 로직을 진행하되 다른 곳에 협력을 얻을 때는 메시지만 던질 수 있게하는 역할을 할 수 있도록 구현을 완료했다.

 

이후 추가리팩토링 과정에서는 자바의 코드를 8버전에서 사용하는 Ramda, Stream형태로 변경하려고 시도해보았고 주요로직 하나를 변경했다.

public void initRandomNumberList() {

        answerNumberList = new ArrayList<>();
        answerNumberList.add(Randoms.pickNumberInRange(1, 9));

        while (answerNumberList.size() < 3) {

            int nowNumber = Randoms.pickNumberInRange(1, 9);
            int isDupleNumber = (int) answerNumberList.stream()
                    .filter(n -> n == nowNumber)
                    .count();

            if (isDupleNumber == 0) {
                answerNumberList.add(nowNumber);
            }
        }
    }

 

반응형
반응형

공부하며 가볍게 정리

람다


람다식은 함수(메서드)를 간단한 식으로 표현하는 것

람다 작성 방법 : 메서드의 이름과 반환타입을 제거하고 →를 블록{} 앞에 추가한다

ex)

int max(int a, int b){
	return a > b ? a : b;
}
(int a, int b) -> {
	return a > b ? a : b;
}

람다식을 활용하여 사용할 때는 함수형 인터페이스를 통해서 람다를 구현할 수 있어야한다.

interface를 자체적으로 선언해서 활용해도 되고, 기존에 있는 Functions 패키지에 들어있는 여러 클래스의 의미를 명확히 아는 것이 좋을 것 같다. 그 이유는 자바에서 지원하는 내부 클래스에는 함수형 인터페이스를 매개로 동작하는 메서드들이 있는데 그 메서드를 사용하기 위해서는 함수형 인터페이스가 어떻게 활용되는지 의미라도 파악을 해야 사용할 수 있을 것이기 때문이다.

함수형 인터페이스로는 대표적으로 네가지가 있고 바로 아래 쓴 메서드가 해당 함수를 실행하는 메서드이다.

  • Supplier<T> - 공급자 : 매개변수는 없고 반환값만 있다
    • get()
  • Consumer<T> - 사용자 : 매개변수는 있고, 반환값이 없다
    • accept()
  • Function<T, R> - 함수 : 일반적인 함수형태, 하나의 매개변수를 받아 결과를 반환
    • apply()
  • Predicate<T> - 매개변수 하나를 받아서 boolean타입으로 반환한다
    • test()

T는 제네릭타입을 뜻하고, R은 리턴타입을 뜻한다.

추가로 Bi가 붙은 형태가 있는데 다른 방법은 똑같고 매개변수가 두개 들어간다는 의미이다.

 

스트림


스트림은 jdk1.8버전부터 도입이 되었는데, 도입 이유는 Collection 인터페이스의 자식인 List, Set, Map과 배열등은 이전 버전까지는 같은 메서드여도 동작하는 형태가 달랐다. 때문에 이를 통일된 방식으로 처리를 하기 위해 표준화해서 등장한 것이다.

ex) 배열, List, Set, Map의 정렬방식이 서로 다름

장점으로는 데이터 소스를 추상화하여 표준화 된 방식으로 사용이 가능하고, 또 원본의 코드를 수정하지 않기 때문에 코드의 재사용성이 높아진다. 또한 간결하게 표현이 가능해 가독성이 높아진다.

단점으로는 단순한 로직에서 데이터의 양이 적다면 순환하는데 드는 비용이 기본형의 래퍼클래스로 인해 더 크다는 것을 인지할 수 있다.(박싱 언박싱 반복)

스트림은 중간연산, 최종연산이 있고, 유의해야 할 점으로는 스트림으로 변환하면 1회만 사용되고 버려지는데, 이러한 특징으로 인해 최종연산을 실행하면 해당 스트림이 사라지는 것을 인지하고 데이터 처리를 해야한다.

중간연산 : 순차적이지 않고 상황에 맞게 커스텀된다.(스트림의 지연연산특징)

최종연산 : 중간연산을 상황에 맞게 활용 이후 출력된 데이터를 최종처리할 연산을 작성

 

Optional<T> - T타입 객체의 래퍼클래스

  • 해당 객체가 null인지 판단을 하기 때문에 nullSafe하다
  • null체크를 하는데 드는 비용이 줄어든다(if문, try catch 사용필요x)

 

 

반응형
반응형

현재 알고리즘 공부를 하고있는데 계속 시간초과가 나서 Scanner를 못쓰게 됐다...

앞으로 많은 문제들이 이러한 제약을 갖고있을 듯 하여 이제서야 공부하게 됐다.

https://spody.tistory.com/60

 

1920번 수 찾기 자바로 풀어 본 짧은 글

1920번 수 찾기 1920번: 수 찾기 (acmicpc.net) 1920번: 수 찾기 첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주

spody.tistory.com

정렬과 이분탐색 모두 사용하는데 시간도 얼마 없는 문제.... 여기서 막혀버렸다

때문에 공부 조금 하고 정리할 겸 올리는 글....

사전준비

1
2
3
4
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
cs
1
public static void main(String[] args) throws NumberFormatException, IOException{
cs

main에는 이렇게 IOExcption을 해줘야한다.

BufferedReader는 Scanner에 비해서 속도가 훨씬 빠르다.

이유는 입력된 데이터가 버퍼를 거쳐 전달돼서 데이터 처리 효율이 올라간다.

때문에 데이터가 많이 들어갈 때는 이 클래스를 활용해서 해결해야한다.

또 StringTokenizer를 사용하는 이유는 BufferedReader는 한 줄씩 입력을 받는 readLine을 사용하게 되는데

"10 5 3 1 10" 등 값을 띄어쓰기로 구분 해 입력받는다 이러한 한 줄을 처리하기 위해 String을 token간격으로 끊어 사용한다는 의미의

StringTokenizer를 사용해 해당 값을 처리해준다.

기본적으로 StringTokenizer을 사용하면 띄어쓰기 별로 10 5 3 1 10 이 값에서 nextToken()메소드를 사용할 때마다

순차적으로 꺼낸다.

ex)

1
2
3
4
5
6
7
8
9
10
11
import java.io.BufferedReader;
 
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int[] a = new int[n];
 
StringTokenizer st = new StringTokenizer(br.readLine());
 
for(int i = 0; i < n; i++) {
    a[i] = Integer.parseInt(st.nextToken());
}
cs

input : 5

           10 5 3 1 10

이 경우

a[0] = token(10)

a[1] = token(5)

a[2] = token(3)

a[3] = token(1)

a[4] = token(10)

이런 식이다.

형태를 잘 기억해두자.

 

반응형
반응형

nextint() 입력 후 nextLine()을 사용 시에 입력관련 오류가 생긴다.

 

이유는 콘솔 입력시에 엔터키를 입력 했을 때만 지금까지 눌렀던 내용이 버퍼에 전달되어 저장이 되는데, nextInt의 메서드

는 int값만 가져간 후 구분자를 받지 않는다.

때문에 이후 \n의 구분자를 통해 입력을 구분하는 nextLine메서드를 사용할 경우 해당 부분에서 문제가 생긴다. nextLine의 첫번째 입력을''\n의 상태로 입력을 받는 것이다.

 

아래는 해당 부분의 코드 예제이다.

1
2
3
4
5
6
7
8
9
        Scanner sc = new Scanner(System.in);
        int n;
        n = sc.nextInt();
        
        String[] s = new String[n];
            
        for(int i = 0; i < n; i++) {    
            s[i] = sc.nextLine(); // 이 부분에서 s[0]에 ''가 저장이된다.
        }
cs

이러한 오류를 해결할 방법은 알아본 바로는 두가지가 있는데,

 

첫번째는 

1
2
3
4
5
6
7
8
9
10
        Scanner sc = new Scanner(System.in);
        int n;
        n = sc.nextInt();
        sc.nextLine();//해당부분 줄     
        
        String[] s = new String[n];
            
        for(int i = 0; i < n; i++) {    
            s[i] = sc.nextLine(); // 이 부분에서 s[0]에 ''가 저장이된다.
        }
cs

이렇게 nextLine()을 한줄 추가해서 방지하는 것과

 

 

1
2
3
4
5
6
7
8
9
        Scanner sc = new Scanner(System.in);
        int n;
        n = n = Integer.parseInt(sc.nextLine());//nextLine을 사용해서 int형식 
        
        String[] s = new String[n];
            
        for(int i = 0; i < n; i++) {    
            s[i] = sc.nextLine(); // 이 부분에서 s[0]에 ''가 저장이된다.
        }
cs

위와 같이 아예 nextLine()을 통해 int형을 입력받는 방법이다.

 

가장 깔끔한 방법은 두번째 방법인 듯 하다.

 

문제풀다가 멘붕와서 서치한 내용을 토대로 정리 완!

 

요약 : nextInt에는 \n의 개행문자가 들어가지 않음. 때문에 다음 nextLine사용 시 해당 enter(\n)구분자를 가져가기에
nextLine을 바로 아랫줄에 넣어 초기화 시켜주거나 Integer.parseInt(sc.nextLine());의 형식으로 입력을 받아야 함

반응형

+ Recent posts