Java심화_실시간 3일차

5w31892p·2022년 12월 2일
0

Java_실시간

목록 보기
7/7

유튜브 영상시청 자료

keyword

Test | 단위테스트
TDD | 리팩터링
CI / CD
JUnit
Thread
call stack
병렬실행


체크리스트

✔ 테스트 코드 필요성과 확인 및 정리
✔ JUnit을 활용한 테스트 코드 작성 가능?
✔ Thread와 Multi Thread를 이해 및 설명 가능?
✔ 동기화(Synchronization) 문제를 설명 가능?
✔ JUnit을 활용해 테스트 코드를 작성 가능?


📜 테스트 코드

  • 의도된대로 정확히 작동하는지 검증 가능
  • 윈도우 기준 ALT + ins 누르고 테스트 클릭
    • 테스트 폴더에 저장됨
    • 만들게 되면 좌측에 실행 버튼 생김
    • 메소드 위에 Annotation

:: 테스트 코드 작성의 장점

  • 개발 과정 중 예상치 못한 문제 미리 발견 할 수 있음
  • 작성한 코드가 의도한대로 작동하는지 검증 할 수 있음
  • 코드의 변경, 추가, 삭제 시 사이드 이펙트 줄일 수 있음
    • 마음 편한 변경, 추가, 삭제 가능
    • 마음 편한 리팩터링 가능 - 돌아가는 코드 건들지마라 X

컴퓨터가 이해하는 코드는 어느 바보나 짤 수 있다.
좋은 프로그래머는 사람이 이해하는 코드를 짠다.
-마틴 파울러-

:: Annotation

Test만 붙어도 아래 다 됨

  • @Test : 테스트 메서드임을 나타냄. 실행 가능
  • @BeforeEach : 각 메서드 전에 실행됨을 나타냄.
  • @BeforeAll : 현재 클래스의 모든 메서드보다 먼저 실행되어야하는 메서드 표시.
  • @AfterAll : 현재 클래스의 모든 메서드보다 나중에 실행되어야하는 메서드 표시.
  • @RepeatedTest : 메서드 반복 호출을 함을 표시.
  • @DisplayName : 사용자 지정 표시 이름을 선언.
    • 테스트 쓰면서 같이 써야함 (작성법 - 설명이 포함된 이름 사용)

-> 테스트 메서드 및 Life Cycle을 잘 이해하고 있어야 우아하게 활용 가능

JUnit 공식 가이드 문서

:: 테스트 코드 작성하는 방법

좋은 테스트 코드는 새로운 기능을 구현, 코드를 리팩터링 하는 데에 자신감과 안정감을 주지만,
의미 없는 테스트 코드를 작성하면 테스트 코드 작성에 들이는 에너지가 낭비가 되고,
테스트 코드를 수정 유지 관리하는 데에 많은 비용이 듦

CI/CD

  • 애플리케이션 개발 단계를 자동화하여 애플리케이션을 더욱 짧은 주기로 고객에게 제공하는 방법
  • 지속적인 통합, 지속적인 서비스 제공, 지속적인 배포

:: 간단하게

  • 테스트에서 복잡한 논리 피하셈
    • 프로젝트가 복잡해짐에 다라 테스트의 시나리오가 꼬이고 테스트 코드의 복잡도가 높아질 수 있음
  • 엣지 케이스 테스트
    • 자주 발생하지 않는 항목을 테스트
    • 오류가 발생해야되는 부분, 예외 처리가 필요한부분 등을 테스트
      • 잘못된 입력 / 누락된 인수 / 빈 데이터 / 호출된 함수의 예외
  • 버그 수정 전에 테스트 작성
    • 버그가 발생했다면 버그를 재현하는 테스트 작성
      • 추후 이 버그 발견하기 좋은 최긔 테스트로 남기 때문
      • 버그도 테스트 코드로 만들어서 확인
  • 설명이 포함된 이름 사용
    • 테스트가 실패했을 때 가장 먼저 보게 되는 것이 이름
    • 길고 설명적인 이름을 두려워하지 말기
  • 한 번에 하나의 요구사항 테스트
    • 요구사항에 맞는 구체적인 이름을 선택할 수 있고 테스트가 덜 부풀어 오르고 읽기 쉽고 유지 관리가 쉬워짐

참고자료 Good-unit-test

:: 백문이불여일타

  • JUnit 활용해, 기능단위 테스트하는 코드 작성
  • @BeforeAll , @BeforeEach
...

class PracticeTest {
	@BeforeAll
  static void setUp() {
      System.out.println("@BeforeAll - executes once before all test methods in this class");
  }

  @BeforeEach
  void init() {
      System.out.println("@BeforeEach - executes before each test method in this class");
  }
}
  • @AfterEach , @AfterAll
...

	@AfterEach
  void tearDown() {
      System.out.println("@AfterEach - executed after each test method.");
  }

  @AfterAll
  static void done() {
      System.out.println("@AfterAll - executed after all test methods.");
  }
}
  • Assertions → 찐 기능 검증
...

	@Test
  @DisplayName("람다 사용")
  void lambdaExpression() {
      List<Integer> numbers = Arrays.asList(1, 2, 3);

      assertTrue(numbers.stream()
              .mapToInt(Integer::intValue)
              .sum() > 5, () -> "Sum should be greater than 5");
  }

...

    • 구현 클래스
...

public class Practice {
    public Integer sumInt(int a, int b) {
        return a + b;
    }
    public Double average(List<Integer> integers) {
        return integers.stream().mapToInt(Integer::intValue).average().getAsDouble();
    }
}

    • 테스트
...

@Test
    @DisplayName("sum 메소드 성공 테스트")
    void sumTest() {
        Integer sumInt = practice.sumInt(1, 2);

        assertNotNull(sumInt);
        assertEquals(sumInt, 3);
    }

  @Test
  @DisplayName("average 메소드 성공 테스트")
  void average() {
      Double average = practice.average(Arrays.asList(1, 2, 3, 5, 6));

      assertNotNull(average);
      assertEquals(average, 3.4);
  }

참고 사이트


📜 스레드

JVM 구조 복습

:: JVM Call Stack

  • main stack frame에서 일어나는 일
  • stack = Thread
  • 함수가 끝나면 데이터 삭제
...

int main(void){
    func1();  // func1() 호출

    return 0;
}

void func1(){
    func2();  // func2() 호출
}

void func2(){
}
...

:: 함수 호출에 의한 스택 프레임의 변화

참고 사이트 - TCP

  1. 프로그램이 실행되면, 가장 먼저 main() 함수가 호출되어 main() 함수의 스택 프레임이 스택에 저장.

  2. func1() 함수를 호출하면 해당 함수의 매개변수, 반환 주소값, 지역 변수 등의 스택 프레임이 스택에 저장.

  3. func2() 함수를 호출하면 해당 함수의 스택 프레임이 추가로 스택에 저장.

  4. func2() 함수의 모든 작업이 완료되어 반환되면, func2() 함수의 스택 프레임만이 스택에서 제거.

  5. func1() 함수의 호출이 종료되면, func1() 함수의 스택 프레임이 스택에서 제거.

  6. main() 함수의 모든 작업이 완료되면, main() 함수의 스택 프레임이 스택에서 제거되면서 프로그램이 종료.

→ Stack 자료구조! Last-In, First-Out

:: Thread와 Multi Thread 프로그래밍

Java = Multi Thread

  • Main Thread
    • 모든 자바 프로그램은 메인 스레드가 psvm을 실행하며 시작
    • main 스레드는 작업 스레드들을 만들어 병렬로 코드들을 실행
  • 프로세스의 종료
    • 싱글 쓰레드 : 메인 쓰레드가 종료하면 프로세스도 종료.
    • 멀티 쓰레드 : 실행 중인 쓰레드가 하나라도 있다면, 프로세스 미종료.
      • 메인이 종료되도 (call stack이 비워져 있어도) 다른 스레드 남아 있다면 안 끝남
        → 잘못된 처리로, 계속 돌고있는 쓰레드가 하나라도 있다면…? ☠️

코드로 스레드 확인

...

public class Main {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
    }
}

코드로 스레드 만들 수 있음

  1. Runnable 인터페이스를 구현하는 방법
  2. Thread 클래스를 상속받는 방법

두 방법 모두 쓰레드를 통해 작업하고 싶은 내용은 run() 메소드에 작성하면 됨

  • Runnable 인터페이스 구현하기
...

public class ImplementRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("Implement Runnable : " + Thread.currentThread().getName());

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.getStackTrace();
        }

        System.out.println("- Implement Runnable End -" );
    }
}

  • Thread 클래스 확장하기
...

public class ExtendThread extends Thread{
    @Override
    public void run() {
        System.out.println("Extend Thread : " + getName()); // 현재 실행 중인 쓰레드의 이름을 반환.

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.getStackTrace();
        }

        System.out.println("- Extend Thread End -" );
    }
}

실행코드

...

public class Main {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());

        Thread implementRunnable = new Thread(new ImplementRunnable());
        implementRunnable.start();

        ExtendThread extendThread = new ExtendThread();
        extendThread.start();

    }
}

run() 메소드 안에 작업 내용 넣기

호출은 start()

:: Thread Scheduling

컴퓨터가 동시에 처리할 수 있는 최대 작업 수는 CPU 코어(core) 수와 같다
  • 스레드의 개수가 코어의 수보다 많을 경우, 스레드를 어떤 순서로 동시성을 실행할 것인가 결정하는 스레드 스케줄링

자바의 스레드 스케줄링은 우선 순위(Priority) 방식과 순환 할당(Round-Robin) 방식 사용.
기본 메커니즘은 Queue 구조를 가짐

Queue : 선입선출(순서대로~~)
  1. 우선 순위 방식 : 우선 순위가 높은 스레드가 실행 상태를 더 많이 가지도록 스케줄링
    • 1~10까지 값을 가질 수 있으며, 기본은 5
  1. 순환 할당 방식
    • 시간 할당량(Time Slice)을 정해서 하나의 스레드를 정해진 시간만큼 실행
    • a, b, c 순선데, a가 끝나고 b가 너무 큰 작업이면 하다가 뒤로 줄 세움

스레드의 일반적인 상태

스레드의 일시정지 상태

상태제어

  • 실행 중인 스레드의 상태 변경
  • 상태 변화를 가져오는 메소드 종류

자바 코드로 스레드 컨트롤 하기



멀티스레드는 동기화 문제 때문에 중요! => 면접에서도 많이 나옴

:: 동기화 (Synchronization) 문제

  • 스레드는 잘 다루면 매우 효율적이지만, 스레드 간섭 및 메모리 일관성 오류라는 두 가지 오류가 발생할 수 있음
  • 이런 오류 방지하는데 필요한 도구가 동기화
  • 두 개 이상의 스레드가 동일한 리소스에 액세스하려고 시도하고 Java 런타임이 하나 이상의 스레드를 느리게 실행하거나 심지어 실행을 일시 중단할 때 발생하는 스레드 경합을 일으킬 수 있다.

참고자료

0개의 댓글