728x90

리눅스 기초

리눅스란?

  1. 컴퓨터 운영체제의 한 종류
  2. 윈도우와는 다르게 오픈 소스 운영체제
  3. 소스코드가 공개되어있기 때문에 다양한 리눅스 기반의 운영체제가 존재

 

특징

  1. 높은 이식성과 확장성(c언어 기반이기 때문)
  2. 안정성과 신뢰성
  3. 계층적 파일 시스템(최상의 디렉터리가 존재하고 모든 것들은 해당 디렉터리 하부에 존재)

 

프롬프트

  1. 컴퓨터가 입력을 기달리고 있음을 가리키기 위해 화면에 나타나는 표시
  2. 일반적으로 리눅스의 프롬프트는 현재 작업 디렉터리, 현재 로그인한 사용자 등에 대한 정보를 표시

 

$는 일반사용자

#은 관리자

 

man ls ls의 명령어에 대한 설명서

엔터 치면 한 줄씩, 스페이스 치면 한 페이지씩


디렉터리 관련 명령어

pwd : 현재 작업 디렉터리 확인
cd : 작업 디렉터리 변경
ls : 디렉터리 내용 확인
mkdir : 디렉터리 생성
rmdir : 디렉터리 제거
mv : 디렉터리 이름 변경
mv : 디렉터리 이동
cp : 디렉터리 복사

pwd : 현재 작업 디렉터리 확인

cd : 작업 디렉터리 변경

  • 절대 경로 : 최상의 디렉터리인 /부터 특정 파일 또는 디렉터리의 경로를 모두 입력
  • 상대 경로 : 현재 작업 디렉터리를 기준으로 특정 파일 또는 디렉터리의 경로를 입력 .은 현재 디렉터리를 의미하고, ..은 상위 디렉터리를 의미한다.

ls : 디렉터리 내용 확인

  • 일반적으로 ls는 -al 옵션과 같이 사용한다.
  • -a는 숨겨진 파일까지 모두 표시, -l 은 좀 더 자세한 결과를 출력한다.
  • -rwxr-xr-x : 파일에 대한 접근 권한
  • 1 : 하드 링크 수
  • root : 파일의 소유자 UID
  • root : 파일의 관리 그룹 GID
  • 4096 : 파일의 크기
  • .dockernev : 파일의 이름 파일 이름 앞에 . 이 붙어있으면 숨겨진 파일이다.

mkdir : 디렉터리 생성

  • -p 옵션을 사용하면 폴더 안에 폴더 안에 폴더 등 여러 폴더를 생성할 수 있다.

rmdir : 디렉터리 제거

  • rmdir은 폴더 안에 파일이 있으면 삭제가 안되고 비어 있어야 삭제가 된다.
  • 따라서 rm -r 옵션을 이용해서 파일을 삭제할 수 있다. rm은 rm -rf로 많이 사용

mv : 디렉터리 이름 변경

  • mv [현재 이름 디렉터리] [변경할 디렉터리 이름]

mv : 디렉터리 이동

  • mv [원본 경로][이동할 경로]

cp : 디렉터리 복사

  • 디렉터리를 통째로 복사할 때는 -r 옵션 사용
  • cp -r [원본 경로][이동할 경로]
728x90

'개발 > Linux' 카테고리의 다른 글

프로세스, 아카이브(tar), 압축  (0) 2022.07.29
특수권한, 쉘 명령어  (0) 2022.07.28
링크, 권한 명령어  (0) 2022.07.27
grep, fgrep, find 명령어  (0) 2022.07.26
리눅스 파일 관련 명령어, vi 명령어  (0) 2022.07.22
728x90

HttpServletRequest는 Http Servlet에 대한 요청 정보를 제공하도록 ServletRequest를 확장한 인터페이스이다.

HttpServletRequest는 서블릿 컨테이너가 생성하며 서블릿의 service() 메서드의 매개변수로 보냅니다.

서블릿의 생명주기(Life Cycle)

1. 서블릿 컨테이너가 서블릿 인스턴스의 init() 메서드를 호출하여 초기화한다.

  • 최초 요청시 한 번만 초기화되며 그 이후로는 이 과정을 생략한다.

2. 서블릿이 초기화된 다음부터 클라이언트가 요청을 처리할 수 있다. 각 요청은 별도의 스레드로 처리하고 이때 서블릿의 service() 메서드를 호출한다.

  • 이 안에서 HTTP 요청을 받고 클라이언트로 보낼 HTTP 응답을 만든다.
  • service()는 Http Method에 따라 doGet() 또는 doPost() 등으로 위임하여 처리한다.

3. 서블릿 컨테이너 판단에 따라 서블릿을 메모리에서 내려야 할 시점에 destroy() 를 호출한다.

public class HelloServlet extends HttpServlet {
  @Override
  public void init() throws ServletExcetion {
    System.out.println("init");
  }
  @Override
  public void doGet(HttpServletReqeust req, HttpServletResponse res) throws ServletExcetion {
    System.out.println("doGet");
  }
  @Override
  public void destory() {
    System.out.println("destroy");
  }
}

서블릿(servlet)은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱 합니다. 그리고 그 결과를 HttpServletRequest 객체에 담아서 제공합니다. 서블릿 덕분에 개발자들은 HTTP 스펙을 편리하게 사용할 수 있습니다. HttpServletRequest의 핵심 기능은 HttpServlet의 요청을 받아서 꺼내서 쓸 수 있다는 것입니다. 그럼 이제 HttpServletRequest로 어떻게 컨트롤러에서 값을 꺼내는지 알아보겠습니다.

<form id='actionForm' action="/board/user" method='post'>
  <input type="text" name="name" value="lusida" />
  <input type="text" name="age" value="26" />
</form>

위처럼 JSP에서 사람 정보를 입력하고 POST로 넘기면 formData 형식처럼 key와 value의 값으로 HttpServletRequest에 담아서 컨트롤러로 전달합니다.

  • content-type : application/x-www/form-urlencoded
  • 메시지 바디에 쿼리 파라미터 형식으로 전달 username=lusida&age=26

GET의 경우 URL 뒤에 /board/user?name=lusida&age=26 형식으로 전달되는데 물음표(?) 뒤의 문자열들을 쿼리스트링 또는 요청 파라미터라고 부릅니다. GET 방식의 경우에도 ?key=value&key=value 형식으로 HttpServletRequest에 담아서 컨트롤러로 전달합니다.

@PostMapping("/user")
public String user(HttpServletRequest request) {
  String name = request.getParameter("name"); // key 값을 이용해서 꺼내올 수 있다.
  String age = request.getParameter("age"); // key 값은 input 에서 설정한 name 값이다.
  
  // 만약에 JSP 에서 설정한 name="userName" 이라는 키값이 여러개인 경우에는
  // getParameterValues() 메서드를 이용하여 배열로 받아올 수 있다.
  String[] names = request.getParameterValues("name");
  
  return REDIRECT_LIST;
}

중요 : key 값은 input에서 설정한 name 값이다.

 

@RequestParam

HttpServletRequest과 동일하게 @RequestParam 은 1:1 방식입니다. 차이점은 HttpServletRequest 대신 @RequestParam이라는 어노테이션을 사용한다는 점입니다.

<form id='actionForm' action="/board/user" method='post'>
  <input type="text" name="name" value="lusida" />
  <input type="text" name="age" value="26" />
</form>
@PostMapping("/user")
public String user(@RequestParam String name, @RequestParam String age) {
  // @RequestParam 뒤에 붙는 매개변수 변수명은 JSP 에서 설정한 name 의 key 값과 동일해야 한다.
  return REDIRECT_LIST;
}

HttpServletRequest와 @RequestParam을 이용하여 받아오는 경우 요청 파라미터가 많아지면 많아질수록 컨트롤러 내부 코드나 매개변수가 증가하여 코드 가독성이 떨어지고, 작성되는 코드 양이 많아집니다. 이러한 문제를 해결하고자 나온 것이커맨드 객체(Command Object)입니다.

 

커맨드 객체

HttpServletRequest를 통해 들어온 요청 파라미터들을 setter 메서드를 이용하여 객체에 정의되어있는 속성에 바인딩이 되는 객체를 의미한다. 커맨드 객체는 보통 VO 나 DTO를 의미하며, HttpServletRequest로 받아오는 요청 파라미터의 key 값과 동일한 이름의 속성들과 setter 메서드를 가지고 있어야 합니다. 

 

어떻게 자동으로 바인딩을 시켜주냐 하면, 스프링이 내부적으로 HttpServletRequest와 커맨드 객체의 setter 메서드를 이용하여 알아서 바인딩시켜줍니다.

@Getter @Setter
public class User {
  private String name;
  private String age;
}
@PostMapping("/user")
public String user(User user, Model model) {
  String name = user.getName();
  String age = user.getAge();
  
  // user 파라미터를 model 에 담는다.
  model.addAttribute("user", user);
  return REDIRECT_LIST;
}

위에서 user 파라미터를 model에 담는 것을 볼 수 있습니다. 이 코드 또한 @ModelAttribute 어노테이션을 사용하여 제거할 수 있습니다.

 

커맨드 객체의 역할

  1. 컨트롤러에서 View로 바인딩 : View 단에서 form:form 태그를 사용하는 경우
  2. View에서 컨트롤러로 바인딩 : View 단에서 input type="text" 혹은 input type="hidden"으로 값을 컨트롤러로 전송하는 경우
  3. 컨트롤러에서 Mapper.xml로 바인딩 : Mapper.xml 에서 title = #{title}, contents = #{contents}처럼 사용하는 경우, 커맨드 객체를 통해 #{변수명}과 커맨드 객체의 필드명을 통해 바인딩해주는 경우

 

@ModelAttribute 

@ModelAttribute는 크게 메서드명 위에 사용되는 경우와 파라미터 옆에 사용되는 경우로 나뉩니다.

@ModelAttribute는 커맨드 객체와 같이 요청 파라미터들을 객체 프로퍼티에 바인딩시켜준다는 것입니다. 하지만 @ModelAttribute를 생략해도 커맨드 객체를 이용해서 바인딩이 되는데, @RequestParam 또한 생략해도 사실상 바인딩이 가능합니다. 그 이유는 스프링이 내부적으로 String이나 int 등은 @RequestParam으로 보고, 그 외의 복잡한 객체들은 @ModelAttribute 가 생략됐다고 간주하기 때문입니다. 하지만 그렇다고 무조건 생략하는 것은 위험합니다.

@PostMapping("/ins")
public String ins(@ModelAttribute User user, Model model) {
  String name = user.getUserName();
  String age = user.getAge();
  
  // user 객체를 모델에 담는 코드를 작성하지 않아도, 담겨져 있다.
  // 내부적으로 model.addAttribute("user", user); 로 담는다.
  // 만약 객체명과 변수명이 @ModelAttribute UserVo user 로 되어있는 경우에는 어떻게 담길까?
  // 클래스명을 기준으로 카멜케이스를 적용하여 model.addAttribute("userVo", user); 로 담는다.
  
  return REDIRECT_LIST;
}

@ModelAttribute의 역할 중 하나는 model 에 객체를 담아준다는 것입니다. 파라미터 객체 옆에 @ModelAttribute를 사용했을 때 얻는 또 다른 이점은 @ModelAttribute 가 붙은 파라미터를 처리할 때는 @RequestParam과 달리 검증(Validation) 작업을 내부적으로 진행합니다.

@RequestParam의 경우 스프링의 기본 타입 변환 기능을 이용해서 요청 파라미터 값을 메서드 파라미터 타입으로 변환하는데, 만약 숫자 타입의 파라미터라면 문자열 타입으로 들어온 요청 파라미터의 타입 변환을 시도하고 실패하면 Http 400 Bad Request 응답이 클라이언트로 가게 됩니다.

하지만 @ModelAttribute의 경우 내부적으로 검증(Validation) 작업을 진행하기 때문에 setter 메서드를 이용하여 값을 바인딩하려고 시도하다가 예외를 만나지만 작업이 중단되면서 Http 400 Bad Request 가 발생하지는 않습니다. 타입 변환에 실패해도 작업은 계속되며 BindingException 타입의 객체에 담겨서 컨트롤러로 전달됩니다. 보통 등록이나, 수정을 처리하는 핸들러 메서드의 경우 다양한 검증을 실시해야 하고, 사용자의 입력 값에 오류가 있을 때에는 이에 대한 처리를 컨트롤러에게 맡겨야 합니다.

따라서 @ModelAttribute를 통해서 폼의 정보를 전달받는 경우 Errors 객체나 BindingResult 객체를 @ModelAttribute 가 붙은 파라미터 바로 뒤에 선언해서 검증 처리를 실시합니다. Errors 나 BindingResult는 자신의 바로 앞에 있는 파라미터 검증에서 발생한 오류들만 전달해주기 때문에 @Valid 나 @Validated, @ModelAttribute 가 붙은 파라미터 바로 뒤에 선언되어야 합니다.

 

메서드 위에 @ModelAttribute 가 사용되는 경우

컨트롤러에서 메서드 위에 @ModelAttribute 가 사용되는 경우는, 해당 컨트롤러 내의 어떠한 핸들러 메서드들보다 먼저 동작하게 됩니다.

/**
 * @ModelAttribute 메서드가 먼저 동작하기 때문에,
 * 다른 핸들러 메서드에서 model 에 담겨져있는 user 키값을 이용하여 user 객체를 꺼내서 쓸 수 있다.
 */
@ModelAttribute("user")
public String initUser() {
  // 내부적으로 model.addAttribute("user", userService.findUser(FIRST_USER_SEQ)); 형태로 담는다.
  return userService.findUser(FIRST_USER_SEQ); 
}

따라서 여러 핸들러 메서드에서 공통으로 쓰이며, View 단에서도 꺼내 쓸 일이 있는 것들은 이런 식으로 처리해서 사용하기도 합니다.

 

참고 블로그

https://medium.com/webeveloper/modelattribute-%EC%99%80-%EC%BB%A4%EB%A7%A8%EB%93%9C-%EA%B0%9D%EC%B2%B4-command-object-42c14f268324

 

@ModelAttribute 와 커맨드 객체(Command Object)

@ModelAttribute 와 커맨드 객체(Command Object), @RequestParam 과 HttpServletRequest

medium.com

 

728x90
728x90

Optional<T>

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

public final class Optional<T> {
   private final T value; // T 타입의 참조변수
      ....
   }
}

NullPointerException 오류의 발생이 없고 if문 코드로 null을 체크 안 해줘도 되는 장점이 있다.

 

Optional<T> 객체를 생성하는 다양한 방법

String str = "abc";
Optional<String> optVal = Optional.of(str);
Optional<String> optVal = Optional.of("abc");
Optional<String> optVal = Optional.of(null); // NullpointerException 발생
Optional<String> optVal = Optional.ofNullable(null); // ok

null 대신 빈 Optional<T> 객체를 사용하자

Optional<String> optVal = null; // 널로 초기화 바람직하지 않음
Optional<String> optVal = Optional.<String>empty(); // 빈 객체로 초기화

Optional 객체의 값 가져오기

Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); // optVal에 저장된 값을 반환, null이면 예외발생
String str2 = optVal.orElse(" "); // optVal에 저장된 값이 null이면 " "반환
String str3 = optVal.orElseGet(String::new); // 람다식 사용가능
String str4 = optVal.orElseThrow(NullPointerException::new); // 널이면 예외발생

isPresent() - Optional 객체의 값이 null이면 false, 아니면 true를 반환

 

728x90
728x90

join()

지정된 시간 동안 특정 스레드가 작업하는 것을 기다린다.

예외처리를 해야 된다.(InterruptedException이 발생하면 작업 재개)

 

yield()

남은 시간을 다음 쓰레드에게 양보하고, 자신(현재 스레드)은 실행 대기한다.

yield()와 interrupt()를 적절히 사용하면, 응답성과 효율을 높일 수 있다.

 

스레드의 동기화

멀티 쓰레드 프로세스에서는 다른 쓰레드의 작업에 영향을 미칠 수 있다.

진행 중인 작업이 다른 스레드에게 간섭받지 않게 하려면 동기화가 필요

동기화하려면 간섭받지 않아야 하는 문장들을 임계 영역으로 설정

임계 영역은 락을 얻은 단 하나의 스레드만 출입가능(객체 1개에 락 1개)

 

synchronized를 이용한 동기화

synchronized로 임계 영역(lock이 걸리는 영역)을 설정하는 방법 2가지

1. 메서드 전체를 임계 영역으로 지정
public sychronized void calcsum() {
   // ...             임계 영역
}
2. 특정한 영역을 임계 영역으로 지정
sychronized(객체의 참조변수) {
   // ...             임계 영역
}

wait()과 notify()

동기화 효율을 높이기 위해 wait(), notify()를 사용

Object클래스에 정의되어 있으며, 동기화 블록 내에서만 사용할 수 있다.

  • wait() - 객체의 lock을 풀고 스레드를 해당 객체의 waiting pool에 넣는다.
  • notify() - waiting pool에서 대기 중인 쓰레드 중의 하나를 깨운다.
  • notifyAll() - waiting pool에서 대기중인 모든 스레드를 깨운다.

람다식

함수(메서드)를 간단한 식으로 표현하는 방법

익명 함수(이름이 없는 함수, anonymous function)

함수와 메서드의 차이

  • 근본적으로 동일. 함수는 일반적 용어, 메서드는 객체지향 개념 용어
  • 함수는 클래스에 독립적, 메서드는 클래스에 종속적

람다식 작성하기

  1. 메서드의 이름과 반환 타입을 제거하고 '->'를 블록{ } 앞에 추가한다.
  2. 반환 값이 있는 경우, 식이나 값만 적고 return문 생략 가능(끝에 ; 안 붙임)
  3. 매개변수의 타입이 추론 가능하면 생략 가능(대부분의 경우 생략 가능)

람다식 작성하기 - 주의 사항

  1. 매개변수가 하나인 경우, 괄호() 생략 가능(타입이 없을 때만)
  2. 블록 안의 문장이 하나뿐 일 때, 괄호 { } 생략 가능(끝에 ; 안 붙임)
  3. 단 하나뿐인 문장이 return문이면 괄호 { } 생략 불가

람다식은 익명 함수가 아니라 익명 객체이다.

 

함수형 인터페이스

함수형 인터페이스 - 단 하나의 추상 메서드만 선언된 인터페이스

함수형 인터페이스 타입의 참조 변수로 람다식을 참조할 수 있음(단, 함수형 인터페이스의 메서드와 람다식의 매개변수 개수와 반환 타입이 일치해야 함)

 

생성자와 메서드 참조

콜론 두 개 (:: – 이중 콜론 연산자)의 정식 명칭은 메서드 참조 표현식(method reference expression)이며, 결론부터 말하자면 람다식에서 파라미터를 중복해서 쓰기 싫을 때 사용합니다.

람다 표현식(expression)에서만 사용 가능하고, 사용 방법은 [인스턴스]::[메서드명(또는 new)]

Supplier<MyClass> s = () -> new MyClass();
Supplier<Myclass> s = MyClass::new;

 

스트림

다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> intStream = list.stream(); // 컬렉션
Stream<String> strStream = Stream.of(new String[] {"a", "b", "c"}); // 배열
Stream<Integer> evenStream = Stream.iterate(0, n -> n + 2); // 0, 2, 4, 6, ...
Stream<Double> randomStream = Stream.generate(Math::random); // 람다식
IntStream intStream = new Random().ints(5); // 난수 스트림(크기가 5)

스트림

스트림이 제공하는 기능 - 중간 연산과 최종 연산

  • 중간 연산 - 연산 결과가 스트림인 연산. 반복적으로 적용 가능
  • 최종 연산 - 연산 결과가 스트림이 아닌 연산. 단 한 번만 적용 가능(스트림의 요소를 소모)

Stream.distinct(). limit(5). sorted(). forEach(System.out::println)

. distinct() : 중간 연산,. limit(5) : 중간 연산,. sorted() : 중간 연산,. forEach(System.out::println) : 최종 연산

스트림은 데이터 소스로부터 데이터를 읽기만 할 뿐 변경하지 않는다.

스트림은 Iterator처럼 일회용이다.(필요하면 다시 스트림을 생성해야 함)

최종 연산 전까지 중간 연산이 수행되지 않는다. - 지연된 연산

 

 

728x90
728x90

열거형

관련된 상수들을 같이 묶어 놓은 것. Java에는 타입에 안전한 열거형을 제공

열거형 정의 방법

  • enum 열거형 이름 { 상수명 1, 상수명 2,...}

열거형 상수의 비교는 == 과 compareTo() 사용 가능

모든 열거형은 Enum의 자손이며, 아래의 메서드를 상속받는다.

메서드 설명
Class<E> getDeclaringClass() 열거형의 class 객체를 반환
String name() 열거형 상수의 이름을 문자열로 반환
int ordinal() 열거형 상수가 정의된 순서를 반환(0부터 시작)
T valueOf(Class<T> enumType, String name) 지정된 열거형에서 name과 일치하는 열거형 상수를 반환

불연속적인 열거형 상수의 경우, 원하는 값을 괄호() 안에 적는다.

  • enum Direction { EAST(1), SOUTH(5), WEST(-1)}

괄호를 사용하려면, 인스턴스 변수와 생성자를 새로 추가해 줘야 한다.

enum Direction {
  EAST(1), SOUTH(5), WEST(-1);
  private final int value; // 정수를 저장할 필드(인스턴스 변수)를 추가
  Direction(int value) {
     this.vlaue = value;  // 생성자를 추가
  }
  public int getValue() {
     return value;
  }
}

열거형의 생성자는 묵시적으로 private이므로, 외부에서 객체 생성 불가

 

메타 애너테이션

메타 애너테이션은 애너테이션을 위한 애너테이션

메타 에너테이션은 java.lang.annotation 패키지에 포함

애너테이션 설명
@Target 애너테이션이 적용가능한 대상을 지정하는데 사용
@Documented 애너테이션 정보가 javadoc으로 작성된 문서에 포함되게 한다.
@Inherited 애너테이션 자손 클래스에 상속되도록 한다.
@Retention 애너테이션이 유지되는 범위를 지정하는데 사용한다.
@Repeatable 애너테이션을 반복해서 적용할 수 있게 한다.

@Target

대상 타입 의미
ANNOTATION_TYPE 애너테이션
CONSTRUCTOR 생성자
FIELD 필드(맴버변수, enum상수)
LOCAL_VARIABLE 지역변수
METHOD 메서드
PACKAGE 패키지
PARAMETER 매개변수
TYPE 타입(클래스, 인터페이스, enum)
TYPE_PARAMETER 타입 매개변수
TYPE_USE 타입이 사용되는 모든 곳

@Retention

유지 정책 의미
SOURCE 소스 파일에만 존재, 클래스파일에는 존재하지 않음
CLASS 클래스 파일에 존재, 실행시 사용 불가 , 기본 값
RUNTIME 클래스 파일에 존재, 실행시에 사용 가능

컴파일러에 의해 사용되는 애너테이션의 유지 정책은 SOURCE이다.

실행 시에 사용 가능한 애너테이션의 정책은 RUNTIME이다.

 

프로세스

실행 중인 프로그램, 자원과 스레드로 구성

 

쓰레드

프로세스 내에서 실제 작업을 수행

모든 프로세스는 최소한 하나의 스레드를 가지고 있다.

 

멀티 스레드의 장단점

장점

  • 시스템 자원을 보다 효율적으로 사용할 수 있다.
  • 사용자에 대한 응답성이 향상된다.
  • 작업이 분리되어 코드가 간결해진다.

단점

  • 동기화에 주의해야 한다.
  • 교착상태가 발생하지 않도록 주의해야 한다.
  • 각 스레드가 효율적으로 고르게 실행될 수 있게 해야 한다. 

쓰레드 구현과 실행

  1. Thread 클래스를 상속
  2. Runnable 인터페이스를 구현

스레드의 실행

스레드를 생성한 후에 start()를 호출해야 쓰레드가 작업을 시작한다.

ThreadEx1_1 t1 = new ThreadEx1_1(); // 쓰레드 t1을 생성한다.
ThreadEx1_1 t2 = new ThreadEx1_1(); // 쓰레드 t2을 생성한다.

t1.start(); // 쓰레드 t1을 실행시킨다.
t2.start(); // 쓰레드 t2를 실행시킨다.

 

main 쓰레드

main 메서드의 코드를 수행하는 쓰레드

스레드는 사용자의 쓰레드와 데몬 쓰레드 두 종류가 있다.

 

스레드의 우선순위

작업의 중요도에 따라 스레드의 우선순위를 다르게 하여 특정 쓰레드가 더 많은 작업시간을 갖게 할 수 있다.

setPriority(숫자);

 

쓰레드 그룹

서로 관련된 스레드를 그룹으로 묶어서 다루기 위한 것

모든 스레드는 반드시 하나의 쓰레드 그룹에 포함되어 있어야 한다.

쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 main 쓰레드 그룹에 속한다.

자신을 생성한 스레드(부모 쓰레드)의 그룹과 우선순위를 상속받는다.

 

데몬 쓰레드

일반 스레드의 작업을 돕는 보조적인 역할을 수행

일반 스레드가 모두 종료되면 자동적으로 종료된다.

가비지 컬렉터, 자동 저장, 화면 자동갱신 등에 사용된다.

무한루프와 조건문을 이용해서 실행 후 대기하다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.

boolean isDaemon() - 스레드가 데몬 스레드인지 확인한다.

void setDaemon(boolean on) - 스레드를 데몬 스레드로 또는 사용자 쓰레드로 변경, 매개변수 on을 true로 지정하면 데몬 스레드가 된다.

setDaemon(boolean on)은 반드시 start()를 호출하기 전에 실행되어야 한다. 그렇지 않으면 IllegalThreadStateException이 발생한다.

 

스레드의 상태

상태 설명
NEW 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태
RUNNABLE 실행 중 또는 실행 가능 상태
BLOCKED 동기화블럭에 의해서 일시정지된 상태(lock이 풀릴 때까지 기다리는 상태)
WAITING, TIMED_WAITING 쓰레드의 작업이 종료되지는 않았지만 실행가능하지 않은(unrunnable) 일시정지 상태. TIMED_WAITING은 일시정지시간이 지정된 경우를 의미
TERMINATED 쓰레드의 작업이 종료된 상태

 

sleep()

현재 스레드를 지정된 시간 동안 멈추게 한다.

예외처리를 해야 한다.(InterruptedException이 발생하면 깨어남)

 

interrupt()

대기상태인 스레드를 실행 대기 상태로 만든다.

 

 

728x90
728x90

개요

지속적인 배포, 블루-그린 배포, 카나리아 배포 등과 같은 애플리케이션 배포 시나리오를 관리하기 위해 정기적으로 여러 배포를 사용합니다. 이 실습에서는 여러 이기종 배포가 사용되는 이러한 일반적인 시나리오를 수행할 수 있도록 컨테이너 확장 및 관리에 대한 실습을 제공합니다.

 

배포 소개

이기종 배포에는 일반적으로 특정 기술 또는 운영 요구 사항을 해결하기 위해 둘 이상의 별개의 인프라 환경 또는 지역을 연결하는 작업이 포함됩니다. 이기종 배포는 배포의 세부 사항에 따라 하이브리드, 멀티 클라우드 또는 퍼블릭-프라이빗이라고 합니다. 이 실습의 목적에 따라 이기종 배포에는 단일 클라우드 환경, 여러 퍼블릭 클라우드 환경(멀티 클라우드) 또는 온프레미스와 퍼블릭 클라우드 환경의 조합(하이브리드 또는 퍼블릭-프라이빗) 내의 지역에 걸친 배포가 포함됩니다.

단일 환경 또는 지역으로 제한된 배포에서는 다양한 비즈니스 및 기술 문제가 발생할 수 있습니다.

  • 최대 리소스 : 모든 단일 환경, 특히 온프레미스 환경에서는 프로덕션 요구 사항을 충족하는 컴퓨팅, 네트워킹 및 스토리지 리소스가 없을 수 있습니다.
  • 제한된 지리적 범위 : 단일 환경에 배포하려면 지리적으로 멀리 떨어져 있는 사람들이 하나의 배포에 액세스해야 합니다. 트래픽은 전 세계의 중앙 위치로 이동할 수 있습니다.
  • 제한된 가용성 : 웹 규모의 트래픽 패턴은 애플리케이션이 내결함성과 탄력성을 유지하도록 요구합니다.
  • 공급업체 종속 : 공급업체 수준 플랫폼 및 인프라 추상화로 인해 애플리케이션을 이식할 수 없습니다.
  • 유연하지 않은 리소스 : 리소스가 특정 컴퓨팅, 스토리지 또는 네트워킹 오퍼링 세트로 제한될 수 있습니다.

이기종 배포는 이러한 문제를 해결하는 데 도움이 될 수 있지만 프로그래밍 방식의 결정론적 프로세스와 절차를 사용하여 설계해야 합니다. 일회성 또는 임시 배포 절차로 인해 배포 또는 프로세스가 취약해지고 장애를 견딜 수 없게 될 수 있습니다. 임시 프로세스는 데이터를 손실하거나 트래픽을 삭제할 수 있습니다. 좋은 배포 프로세스는 반복 가능해야 하며 프로비저닝, 구성 및 유지 관리를 위해 입증된 접근 방식을 사용해야 합니다. 이기종 배포에 대한 세 가지 일반적인 시나리오는 다중 클라우드 배포, 온프레미스 데이터 프론팅 및 CI/CD(지속적 통합/지속적 전달) 프로세스입니다.

 

1. 배포

gcloud container clusters create bootcamp --num-nodes 5 --scopes "https://www.googleapis.com/auth/projecthosting,storage-rw"

5개의 노드가 있는 클러스터를 만듭니다.

 

kubectl explain deployment

deployment에 대한 설명을 보여주는 명령어입니다.

 

kubectl explain deployment --recursive

--recursive 옵션을 사용하면 모든 필드에 대한 doployment 내용을 확인할 수 있습니다.

 

deployments/auth.yaml구성 파일을 업데이트합니다.

spec:
  container:
	image: "kelseyhightower/auth:1.0.0"

"kelseyhightower/auth:2.0.0"을 "kelseyhightower/auth:1.0.0"으로 업데이트를 해줍니다.

 

kubectl create -f deployments/auth.yaml

kubectl create 인증 배포를 생성하기 위해 명령을 실행하면 배포 매니페스트의 데이터를 준수하는 하나의 포드가 생성됩니다. 이는 replicas필드에 지정된 수를 변경하여 Pod 수를 확장할 수 있음을 의미합니다.

 

kubectl get deployments

배포를 생성한 후에는 생성되었는지 확인할 수 있습니다.

 

kubectl get replicasets

배포가 생성되면 Kubernetes는 배포용 ReplicaSet을 생성합니다. 배포를 위해 ReplicaSet이 생성되었는지 확인할 수 있습니다.

 

kubectl get pods

마지막으로 배포의 일부로 생성된 파드를 볼 수 있습니다. ReplicaSet이 생성될 때 Kubernetes에 의해 단일 Pod가 생성됩니다.

 

kubectl create -f services/auth.yaml

이제 인증 배포를 위한 서비스를 만들 차례입니다. 위의 명령어를 사용해서 인증 서비스를 만듭니다.

 

kubectl create -f deployments/hello.yaml
kubectl create -f services/hello.yaml

hello Deployment를 만들고 expose 합니다.

 

kubectl create secret generic tls-certs --from-file tls/
kubectl create configmap nginx-frontend-conf --from-file=nginx/frontend.conf
kubectl create -f deployments/frontend.yaml
kubectl create -f services/frontend.yaml

frontend 배포를 만들고 노출합니다.

 

kubectl get services frontend
curl -ks https://<EXTERNAL-IP>

외부 IP에 curl 명령을 치면 Hello라고 잘 나오는 것을 확인하실 수 있습니다.

 

2. 확장

이제 배포가 생성되었으므로 확장할 수 있습니다. 

kubectl scale deployment hello --replicas=5

kubectl scale의 replicas 필드는 위의 명령어를 사용하여 쉽게 업데이트할 수 있습니다.

 

kubectl get pods | grep hello- | wc -l
kubectl scale deployment hello --replicas=3
kubectl get pods | grep hello- | wc -l

deployment가 업데이트되면 Kubernetes는 연결된 ReplicaSet을 자동으로 업데이트하고 새 Pod를 시작하여 총 Pod 수가 5가 되도록 한다. 이제 5개의 helloPod가 실행 중인지 확인합니다. 확인하는 명령어는 kubectl get pods | grep hello- | wc -l입니다. kubectl scale deployment hello --replicas=3은 애플리케이션의 replicas를 3으로 축소하는 명령어입니다.

 

롤링 업데이트

배포는 롤링 업데이트 메커니즘을 통해 이미지를 새 버전으로 업데이트하는 것을 지원합니다. Deployment가 새 버전으로 업데이트되면 새 ReplicaSet을 생성하고 이전 ReplicaSet의 복제본이 감소함에 따라 새 ReplicaSet의 복제본 수를 천천히 늘립니다.

 

deployments/auth.yaml구성 파일을 업데이트합니다.

spec:
  container:
	image: "kelseyhightower/auth:2.0.0"

"kelseyhightower/auth:1.0.0"을 "kelseyhightower/auth:2.0.0"으로 업데이트를 해줍니다.

편집기에서 저장하면 업데이트된 배포가 클러스터에 저장되고 Kubernetes가 롤링 업데이트를 시작합니다.

 

kubectl get replicaset
kubectl rollout history deployment/hello

 

실행 중인 롤아웃에서 문제가 감지되면 일시 중지하여 업데이트를 중지합니다.

kubectl rollout pause deployment/hello
kubectl rollout status deployment/hello
kubectl get pods -o jsonpath --template='{range .items[*]}{.metadata.name}{"\t"}{"\t"}{.spec.containers[0].image}{"\n"}{end}'

1번째 명령어는 중지시키는 명령어입니다. 2번째 명령어는 deployment의 상태를 확인하는 명령어입니다.

3번째 명령어는 Pod에서 확인하기 위한 명령어입니다.

 

kubectl rollout resume deployment/hello
kubectl rollout status deployment/hello

롤아웃이 일시 중지되어 일부 포드는 새 버전이고 일부 포드는 이전 버전입니다. resume명령 을 사용하여 롤아웃을 계속할 수 있습니다.

status롤아웃이 완료되면 위의 status 명령어를 실행할 때 "hello" 성공적으로 롤아웃이 되었습니다라고 출력되어야 합니다.

 

kubectl rollout undo deployment/hello
kubectl rollout history deployment/hello
kubectl get pods -o jsonpath --template='{range .items[*]}{.metadata.name}{"\t"}{"\t"}{.spec.containers[0].image}{"\n"}{end}'

새 버전에서 버그가 감지되었다고 가정합니다. 새 버전에는 문제가 있는 것으로 추정되므로 새 Pod에 연결된 모든 사용자는 이러한 문제를 겪을 것입니다.

조사한 다음 제대로 수정된 버전을 릴리스할 수 있도록 이전 버전으로 롤백하고 싶을 것입니다.

다음 명령을 사용 rollout하여 이전 버전으로 롤백합니다.

 

 

카나리아 배포

사용자의 하위 집합을 사용하여 프로덕션에서 새 배포를 테스트하려는 경우 카나리아 배포를 사용합니다. Canary 배포를 사용하면 소규모 사용자 하위 집합에 대한 변경 사항을 릴리스하여 새 릴리스와 관련된 위험을 완화할 수 있습니다.

카나리아 배포는 새 버전이 포함된 별도의 배포와 정상적인 안정적인 배포와 카나리아 배포를 모두 대상으로 하는 서비스로 구성됩니다.

 

kubectl create -f deployments/hello-canary.yaml
kubectl get deployments

Canary 배포가 생성된 후에는 hello 하고 hello-canary의 두 개의 배포가 있어야 한다.

 

curl -ks https://`kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"`/version

hello요청에 의해 제공되는 버전을 확인할 수 있습니다. 이것을 여러 번 실행하면 일부 요청이 hello 1.0.0에서 제공되고 작은 하위 집합(1/4 = 25%)이 2.0.0에서 제공되는 것을 볼 수 있습니다.

 

블루 - 그린 배포

롤링 업데이트는 오버헤드를 최소화하고 성능에 미치는 영향을 최소화하며 가동 중지 시간을 최소화하면서 애플리케이션을 천천히 배포할 수 있기 때문에 이상적입니다. 새 버전이 완전히 배포된 후에만 해당 새 버전을 가리키도록 로드 밸런서를 수정하는 것이 유리한 경우가 있습니다. 이 경우 블루-그린 배포가 올바른 방법입니다.

Kubernetes는 두 개의 개별 배포를 생성하여 이를 달성합니다. 하나는 이전 "파란색" 버전용이고 다른 하나는 새 "녹색" 버전용입니다. hello"파란색" 버전에 대해 기존 배포를 사용합니다. 배포는 라우터 역할을 하는 서비스를 통해 액세스 됩니다.새로운 "그린" 버전이 실행되고 나면 서비스를 업데이트하여 해당 버전을 사용하도록 전환합니다.

블루-그린 배포의 주요 단점은 애플리케이션을 호스팅 하는 데 필요한 클러스터에 최소 2배의 리소스가 필요하다는 것입니다. 한 번에 두 버전의 애플리케이션을 모두 배포하기 전에 클러스터에 충분한 리소스가 있는지 확인하십시오.

 

kubectl apply -f services/hello-blue.yaml

먼저 서비스를 업데이트합니다.

 

kubectl create -f deployments/hello-green.yaml
curl -ks https://`kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"`/version
kubectl apply -f services/hello-green.yaml
curl -ks https://`kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"`/version

녹색 배포가 있고 제대로 시작되면 현재 버전 1.0.0이 여전히 사용 중인지 확인합니다. 이제 새 버전을 가리키도록 서비스를 업데이트합니다. 서비스가 업데이트되면 "그린" 배포가 즉시 사용됩니다. 이제 새 버전이 항상 사용 중인지 확인할 수 있습니다.

 

kubectl apply -f services/hello-blue.yaml
curl -ks https://`kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"`/version

필요한 경우 동일한 방식으로 이전 버전으로 롤백할 수 있습니다. "파란색" 배포가 계속 실행되는 동안 서비스를 이전 버전으로 다시 업데이트하기만 하면 됩니다. 서비스를 업데이트하면 롤백이 성공한 것입니다. 다시 한 번 올바른 버전이 사용 중인지 확인합니다.

728x90
728x90

HashSet

객체를 저장하기 전에 기존에 같은 객체가 있는지 확인

같은 객체가 없으면 저장하고, 있으면 저장하지 않는다.

 

TreeSet

이진 탐색 트리로 구현. 범위 탐색과 정렬에 유리

이진트리는 모든 노드가 최대 2개의 하의 노드를 갖는다.

각 요소(Node)가 나무 형태로 연결

 

이진 탐색 트리

부모보다 작은 값은 왼쪽 큰 값은 오른쪽에 저장

데이터가 많아질수록 추가, 삭제에 시간이 더 걸림

 

HashMap

Map인터페이스를 구현한 대표적인 컬렉션 클래스

순서를 유지하려면, LinkedHashMap클래스를 사용하면 된다.

해싱 기법으로 데이터를 저장, 데이터가 많아도 검색이 빠르다.

Map인터페이스를 구현. 데이터를 키와 값의 쌍으로 저장

 

TreeMap

범위 검색과 정렬에 유리한 컬렉션 클래스

HashMap보다 데이터 추가, 삭제에 시간이 더 걸림

 

제네릭

컴파일 시 타입을 체크해 주는 기능

객체의 타입 안정성을 높이고 형 변환의 번거로움을 줄여줌

참조 변수와 생성자의 대입된 타입은 일치해야 한다.

ArrayList<Tv> list = new ArrayList<Tv>(); // ok
ArrayList<Product> list = new ArrayList<Tv>(); // error

제네릭 클래스 간의 다형성은 성립

List<Tv> list = new ArrayList<Tv>(); // ok
List<Tv> list = new LinkedList<Tv>(); // ok

매개변수의 다형성도 성립

extends로 대입할 수 있는 타입을 제한

 

제네릭 용어

Box <T> 제네릭 클래스 'T의 Box' 또는 'T Box'라고 읽는다.

T 타입 변수 또는 타입 매게 변수 (T는 타입 문자)

Box 원시 타입

 

제네릭 제약

타입 변수에 대입은 인스턴스 별로 다르게 가능

static 멤버에 타입 변수 사용 불가

배열 생성할 때 타입 변수 사용불가. 타입 변수로 배열 선언은 가능

 

와일드카드

하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능

<? extends T> : 와일드카드의 상한 제한 T와 그 자손들만 가능

<? super T> : 와일드 카드의 하한 제한 T와 그 조상들만 가능

<?> : 제한 없음. 모든 타입이 가능 

 

 

728x90
728x90

컬렉션

여러 객체(데이터)를 모아놓은 것을 의미

 

프레임웍

표준화 정형화된 체계적인 프로그래밍 방식

 

컬렉션 프레임웍

컬렉션(다수의 객체)을 다루기 위한 표준화된 프로그래밍 방식

컬렉션을 쉽고 편리하게 다룰 수 있는 다양한 클래스를 제공

java.util 패키지에 포함

 

1. List

순서가 있는 데이터의 집합. 데이터의 중복을 허용한다.

구현 클래스 : ArrayList, LinkedList, Stack, Vector 등

2. Set

순서를 유지하지 않는 데이터의 집합. 데이터의 중복을 허용하지 않는다.

구현 클래스 : HashSet, TreeSet 등

3. Map

키와 값의 쌍으로 이루어진 데이터의 집합

순서는 유지되지 않으며, 키는 중복을 허용하지 않고, 값은 중복을 허용한다.

구현 클래스 : HashMap, TreeMap, HashTable, Propertise 등

 

ArrayList

ArrayList는 기존의 Vector를 개선한 것으로 구현원리와 기능적으로 동일

ArrayList와 달리 Vector는 자체적으로 동기화 처리가 되어있다.

List인터페이스를 구현하므로, 저장순서가 유지되고 중복을 허용한다.

데이터의 저장공간으로 배열을 사용한다.

 

ArrayList에 저장된 객체의 삭제과정

  1. 삭제할 데이터 아래의 데이터를 한 칸씩 위로 복사해서 삭제할 데이터를 덮어쓴다.
  2. 데이터가 모두 한 칸씩 이동했으므로 마지막 데이터는 null로 변경한다.
  3. 데이터가 삭제되어 데이터의 개수가 줄었으므로 size를 감소시킨다.

배열의 장점

배열은 구조가 간단하고 데이터를 읽는 데 걸리는 시간이 짧다

배열의 단점

크기를 변경할 수 없다. 크기를 변경해야 하는 경우 새로운 배열을 생성 후 데이터를 복사해야 함

비순차적인 데이터의 추가, 삭제에 시간이 많이 걸린다.

 

LinkedList 

배열과 달리 LinkedList는 불연속적으로 존재하는 데이터를 연결

데이터의 삭제 : 단  한 번의 참조 변경만으로 가능

데이터의 추가 : 한 번의 Node객체 생성과 두 번의 참조 변경만으로 가능

순차적으로 데이터 추가/삭제 : ArrayList가 빠름

비순차적으로 데이터 추가/삭제 : LinkedList가 빠름

접근 시간 : ArrayList가 빠름

 

Comparable : 기본 정렬 기준을 구현하는데 사용

Comparator : 기본 정렬기준 외의 다른 기준으로 정렬하고자 할 때 사용

compare()와 compareTo()는 두 개체의 비교 결과를 반환하도록 작성 

 

HashSet

set인터페이스를 구현한 대표적인 컬렉션 클래스

순서를 유지하려면, LinkedHashSet클래스를 사용하면 된다.

 

TreeSet

범위 검색과 정렬에 유리한 컬렉션 클래스

HashSet보다 데이터 추가, 삭제에 시간이 더 걸림

728x90

+ Recent posts