이전 포스팅에선 다음과 같이 알아 봤는데요

  • Thread 생성
  • Thread 동기화 블록

이번에는 Thread가 제공하는 몇가지 메서드를 확인해보고
Thread 상태를 제어하는 방법에 대해 알아보도록 하겠습니다


Thread 상태

스레드를 생성하고 start() 메서드를 호출하면 곧바로 스레드가 실행되는 것처럼 보이지만, 사실은 '실행대기' 상태가 됩니다.
실행대기 상태란 아직 스케줄링이 되지 않아 실행을 기다리고 있는 상태를 말합니다.
실행대기 상태에 있는 스레드 중에서 스케줄링으로 선택된 스레드가 비로서 CPU를 점유하고 run() 메서드를 실행하게 됩니다.
이때를 실행 상태라고 합니다.

아래의 사진으로 스레드의 대략적인 상태를 살펴 보겠습니다

실행 상태의 스레드는 다시 실행대기 상태로 돌아갈 수 있고
실행, 실행대기 상태를 번갈아가며 자신의 run() 메서드를 수행합니다.
run() 메서드가 종료되면 스레드의 실행은 멈추게 됩니다.
이 상태를 종료 상태라고 합니다

경우에 따라서 ‘실행상태’에서 ‘일시정지’ 상태로 가기도 하는데,
일시정지 상태에서는 스레드가 실행할 수 없는 상태입니다.
일시정지 상태로는 WAITING, TIMED_WAITING, BLOCKED가 있습니다.
스레드가 다시 실행 상태로 가기 위해서는 일시정지 상태에서 실행대기 상태가 되어야 합니다.

상태 Enum 상수 설명
객체 생성 NEW 레드 객체 생성, 아직 start() 메서드 호출되지 전의 상태
실행대기 RUNNABLE 실행 상태로 언제든지 갈 수 있는 상태
일시정지 WAITING 다른 스레드가 통지(notify) 할 때까지 기다리는 상태
일시정지 TIMED_WAITING 주어진 시간 동안 기다리는 상태
일시정지 BLOCKED 사용하고자 하는 객체의 락이 풀릴 때까지 기다리는 상태
종료 TERMINATED 실행을 마친 상태

스레드의 상태 제어

사용자는 미디어 플레이어에서 동영상을 보다가 일시 정지 시킬수도 있고, 종료시킬수도 있습니다
정지는 다시 동영상을 보겠다는 의미로 스레드를 일시정지 상태로 만들어야 합니다
종료는 더 이상 동영상을 보지 않겠다는 의미이므르 미디어 플레이어는 스레드를 종료 상태로 만들어야 합니다
이와 같이 스레드의 상태를 변경하는 것은 스레드 상태 제어라고 합니다

아래는 스레드 상태를 제어하는 메서드입니다. 하나씩 메서드를 알아보도록 하겠습니다

일시정지 (sleep)

sleep() - 주어진 시간동안 일시 정지 상태로 만들고, 주어진 시간이 지나면 자동적으로 실행대기 상태가 됩니다
try {
    Thread.sleep(3000);
} catch (InterruptedException e) {
    e.printStackTrace();
} 

Thread.sleep(3000) 메서드를 통해 3초간 ‘일시정지’ 상태가 되고 3초가 지나면 다시 ‘실행대기’ 상태로 돌아오게 됩니다.

실행 양보 (yield)

yield() - 실행중에 우선순위가 높은 또는 동일한 다른 스레드에게 실행을 양보하고 실행 대기 상태가 됩니다
if (work) {
    System.out.println(getName() + " 스레드 작업 내용");
} else {
    Thread.yield();
}

yield() 메서드 호출시 해당 Thread는 우선 순위가 높거나 동일한 Thread에게 실행을 양보하고 실행 대기 상태로 전환됩니다

다른 스레드의 종료를 기다림 (join)

join() -메서드를 호출한 스레드는 일시 정지 상태가 됩니다. 실행 대기 상태로 가려면 join() 메서드를 멤버로 가지는 스레드가 종료되거나, 매개값으로 주어진 시간이 지나야 합니다. 이부분은 예제를 보도록 하겠습니다
SumThread
public class SumThread extends Thread {
    private long sum;
    public long getSum() {
        return sum;
    }

    public void run() {
        for (int i = 1; i <= 100; i++) {
            sum += i;
        }
    }
}
JoinExample
public class JoinExample {
    public static void main(String[] args) {
        SumThread sumThread = new SumThread();
        sumThread.start();

        try {
            sumThread.join();
        } catch (Exception e) {
        }

        System.out.println("합 : " + sumThread.getSum());
    }
}

sumthread.join()을 걸어 두었기 때문에 메인 Thread는 합연산이 끝날때까지 기다리게 됩니다. 따라서, 결과값은 5050이 나오게 됩니다

여기서 의문이 들 수 있습니다. Thread.sleep(3000)으로도 충분히 가능한데 무엇이 차이인가? sleep은 일정 시간동안만 실행대기 상태로 만들어줍니다. join의 경우 thread의 작업을 처리할때까지 묶어 둘 수 있기 때문에 멀티스레드 환경에서 동기화를 맞춰야 할 경우 유용하게 사용할 수 있습니다.

스레드간 협업 (wait, notify, notifyAll)

notify, notifyAll - 동기화 블록 내에서 wat 메서드에 의해 일시 정지 상태에 있는 스레드를 실행 대기 상태로 만들어 줍니다
wait - 동기화 블록 내에서 스리드를 일시 정지 상태로 만듭니다. 매개값으로 시간이 주어지면 일정 시간이 지난후 자동으로 실행대기 상태가 되고, 시간이 주어지지 않으면 notify, notifyAll 메서드에 의해 실행 대기 상태로 갈 수 있습니다

스레드 종료 (stop, interrupt)

Thread는 자신의 run() 메서드가 모두 실행되면 자동으로 종료됩니다. 경우에 따라서 Thread를 즉시 종료할 필요가 있습니다

stop - 갑자기 종료하면 스레드가 사용중이던 자원들이 불안전한 상태로 남겨지기 때문에 deprecated 되었습니다
interrupt - 일시 정지 상태의 스레드에서 InterruptedException 예외를 발생시켜, 예외 처리 코드(catch)에서 실행 대기 상태로 가거나 종료 상태로 갈 수 있도록 합니다

참조

  • '이것이 자바다' - 신용권님 저
블로그 이미지

사용자 yhmane

댓글을 달아 주세요

들어가며

우리는 하나의 프로그램, 프로세스로 두 가지 이상의 작업을 처리할 수 있다는 것을 알고 있습니다.
스레드를 이용하면 두가지 이상의 일을 동시에 할 수 있는데요,
이번 포스팅에선 Java를 이용해서 스레드를 생성하는 방법을 알아보도록 하겠습니다.


프로세스와 스레드

스레드를 다루기 전에 필수적인 용어만 짚고 넘어가도록 하겠습니다

  • 프로그램은 파일 시스템에 존재하는 실행파일 (*.exe)
  • 프로세스는 운영체제에서 실행중인 애플리케이션
  • 스레드는 프로세스 내에서 실행되는 여러 흐름의 단위

간단히 정리하면, 프로그램은 실행파일입니다.
이 실행파일은 메모리에 올린 것은 프로세스라고 하는데요
프로세스는 n개의 thread로 구성되어 있습니다.

운영체제의 대한 추가적인 설명은 생략하도록 하고, 자세히 설명되어 있는 링크를 달아두도록 하겠습니다.
스레드, 프로세스의 대한 개념 정리가 필요하시다면 한번씩 읽어보세요 😄
Process와 Thread 이야기. 프로세스(Process) | by Charlezz | Medium


Java Thread 생성

Java에서 Thread를 생성하는 방법은 두가지가 존재합니다

  1. Thread 클래스를 상속 받아 run 메서드를 오버라이딩 하는것
  2. Runnable 인터페이스를 Implements 하여 run 메서드를 정의하는것

먼저, Thread 상속

public class MyThread extends Thread {
    private int index;
    public MyThread(int index) {
        this.index = index;
    }

    @Override
    public void run() {
        System.out.println(this.index + " thread start");
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.index + " thread end");
    }
}

다음으로, Runnable 인터페이스 implements

public class MyRunnable implements Runnable {

    private int index;
    public MyRunnable(int index) {
        this.index = index;
    }

    @Override
    public void run() {
        System.out.println(this.index + " thread start");
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.index + " thread end");
    }
}

실행

public class MyThreadMain {
    public static void main(String[] args) throws  {
          // thread
        for (int i = 0; i < 10; i++) {
            Thread myThread = new MyThread(i);
            myThread.start();
        }

          // runnable
        for (int i = 10; i < 20; i++) {
            Runnable runnable = new MyRunnable(i);
            Thread runnableThread = new Thread(runnable);
            runnableThread.start();
        }
    }
}

이외에도 단순한 Thread의 처리라면 익명클래스나 람다를 이용하는 방법도 있습니다.
Java 8버전 이상을 사용하신다면 익명클래스 보단 람다 사용을 권장합니다!!

new Thread() {
    @Override
    public void run() {
        System.out.println("anonymous class thread");
    }
}.start();

new Thread(() -> System.out.println("lambda thread")).start();

 


start vs run

우리는 run() 메서드를 재정의하였지만, 정작 thread 사용시에는 start() 메서드를 이용하였습니다.
그 이유는 jvm의 메모리 구조때문인데요, 아래의 사진을 잠시 살펴보도록 하겠습니다.

jvm은 ‘data, code, stack, heap’ 영역으로 구성되어 있습니다.
여기서 프로세스 내부의 thread들은 data, code, heap 영역을 공유하게 됩니다.

다만, run() 메서드는 메인쓰레드의 call-stack을 공유하여 작업을 하지만
start() 메서드는 각 쓰레드마다 call-stack을 새로 만들어 작업을 합니다.

run() 메서드는 thread가 순번을 기다리며 대기하고 있기에 하드웨어의 성능을 적절히 사용할 수 없습니다.
따라서 쓰레드의 작업을 적적히 분배하기 위해서는 start() 메서드를 이용해야 합니다


출처

블로그 이미지

사용자 yhmane

댓글을 달아 주세요

Generic

들어가며

제네릭(Generic)은 Java5부터 새로 추가된 내용으로,
제네릭 타입을 이용하여 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있게 되었습니다.
컬렉션, 람다식, 스트림, NIO에서 널리 사용되고 많은 API 도큐먼트에서 제네릭 표현이 사용되므로 정확한 학습이 필요합니다.

개인적으로, Java를 학습하며 Generic은 가장 이해가 어려웠던 파트중 하나로 기억합니다. 이번 포스팅에서는 신용권님의 ‘이것이 자바다’ 13장 Generic을 기반으로 Generic 클래스, 메서드, 형변환 등에 대해서 정리하도록 하겠습니다.


제네릭의 장점

컴파일시 강한 타입 체크를 할 수 있다

자바 컴파일러는 잘못 사용된 타입을 미리 체크하여 주는데, 제니릭 코드에 대해 강한 타입 체크를 한다.
따라서 실행 이전에 컴파일 단계에서 에러를 체크해 준다

타입 변환(casting)을 제거한다

generic을 사용하지 않을 경우

List strList = new ArrayList();
strList.add("hi");
Object strObject = strList.get(0); 
String str = (String) strList.get(0);

generic을 사용할 경우

List<String> strList = new ArrayList<String>();
strList.add("hi");
String str = strList.get(0);

<> 안에 Type을 지정하여 줌을로써, 요소를 가져올때 형변환 과정이 필요 없어진다


제네릭 타입(class, interface)

제네릭 타입은 타입을 파라미터로 가지는 클래스와 인터페이스를 말합니다.

public class 클래스명<T> {...}
public interface 인터페으스명<T> {...}

Generic 표기 방법


일반적으로 Java 진영에서는 위와 같은 컨벤션을 지키려고 한다. 다만, 경우에 따라서 적절한 Name을 지정해 주는 것도 좋다.

다음은 예제를 통해서 간단히 Generic class와 interface를 만들어 보자

Generic class

public class GenericClass<T> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

사용

GenericClass<String> strObject = new GenericClass<String>();
strObject.setValue("Hello, Yhmane!");
System.out.println(strObject.getValue());

GenericClass<Integer> intObject = new GenericClass<Integer>();
intObject.setValue(31);
System.out.println(intObject.getValue());

Generic Interface

public interface QueryResult<ID> {
    ID id();
}

Store.java

public class Store implements QueryResult<Long> {
    private Long storeId;
    private String name;
    private String address;

    public Store(Long storeId, String name, String address) {
        this.storeId = storeId;
        this.name = name;
        this.address = address;
    }

    @Override
    public Long id() {
        return storeId;
    }

    @Override
    public String toString() {
        return "Store{" +
            "storeId=" + storeId +
            ", name='" + name + '\'' +
            ", address='" + address + '\'' +
            '}';
    }
}

User.java

public class User implements QueryResult<Long> {
    private Long userId;
    private String name;
    private String email;

    public User(Long userId, String name, String email) {
        this.userId = userId;
        this.name = name;
        this.email = email;
    }

    @Override
    public Long id() {
        return userId;
    }

    @Override
    public String toString() {
        return "User{" +
            "userId=" + userId +
            ", name='" + name + '\'' +
            ", email='" + email + '\'' +
            '}';
    }
}

사용

User user = new User(1L, "윤호", "test1234@gmail.com");
Store store = new Store(1L, "윤호가게", "강남구 봉은사로");
System.out.println(user);
System.out.println(store);

generic 메서드

제네릭 메서드는 매개 타입과 리턴 탕비으로 타입 파라미터를 갖는 메서드를 말한다.

public <타입파라미터...> 리턴타입 메서드명(매개변수, ...) {...}

Util.java

public class Util {
    public static <T> Box<T> boxing(T t) {
        Box<T> box = new Box<T>();
        box.set(t);
        return box;
    }
}

Box.java

public class Box<T> {
    private T t;

    public T get() {
        return t;
    }

    public void set(T t) {
        this.t = t;
    }
}

사용
은 명시적으로 사용해도 되고 사용하지 않아도 된다

Box<Integer> box1 = Util.<Integer>boxing(100);
Box<String> box2 = Util.boxing("홍길동");

제네릭 타입의 제한과 와일드카드

제네릭도 마찬가지로 extends와 super를 이용할 수 있다

<T extends 상위타입>
<T super 하위타윕>

<?> // 와일드카드
<? extends 상위타입>
<? super 하위타입>

이해를 돕기위해 책에 있는 간단한 예제를 가져와 봤습니다.

Course<?> // Person, Worker, Student, HightStudent가 올수 있습니다
Course<? extends Student> // Student, HighStudent만 올 수 있습니다
Course<? super Worker> // Worker와 Person만 올 수 있습니다

참조

블로그 이미지

사용자 yhmane

댓글을 달아 주세요

주니어 개발자의 2020년 회고

2020년 회고이지만, 그동안 나를 되돌아 볼 시간이 없었기에, 3년간의 과정을 돌이켜 보려고 작성한다.
물론, 앞으로 매년 회고를 작성할 계획이다!!


SI 생활의 마침표, 서비스 회사로의 이직

개발자로서 시작한 것은 2017년 12월!!

나의 첫 회사는 솔루션 회사였다. 회사는 신논현역 금방의 위치하였고, 언덕을 한참 올라가 위치한 곳이였다.
사실 위치는 중요하지 않았다. 왜냐하면 본사에서 일하는 시간보다 고객사에 상주하는 기간이 더 길었기 때문이다... 껄껄껄

솔루션이라고 하였지만 주업무는 커스터마이징이였다. 즉, 솔루션을 가장한 SI(?) 였다 ...

 

그렇게 2년이라는 시간이 지났을까? 난 대리의 직함을 달게 되었고, 어느덧 고객사의 웬만한 요구사항은 협의하여 무리없이 구현하는 단계에 이르었다. SI였지만, 나름 워라벨은 챙길 수 있었기에 야근은 하지 않았다. 다만, 개발자로서 성장의 한계가 왔다고 생각을 하게 되었다.

이직할 타이밍인가?

내가 생각하는 이직 타이밍 신호

  • (1) 근속기간이 3년 이상
  • (2) 개발자로서의 성장
  • (3) 회사가 재정적으로 불안정한 상태 (고정 매출이 없거나, 투자금 확보가 어려움)
  • (4) 잦은 야근, 집과 회사와의 거리
  • (5) 기타 부가적인 요인

나의 경우 (2), (4) 의 문제가 있었다. 야근은 거의 하지 않았지만 파견으로 인해 충북이나, 인천에서 숙소 생활을 했던 경험이 있다.
또한 SI 특성상 리팩토링보다 기능 구현에 초점이 맞춰져 있기에 높은 양질의 코드를 작성하기에는 좋은 조건은 아니였다.
이러한 근거를 바탕으로 난 이직을 결심하게 되었다.

서비스 기업 이직 성공!

신입일 때는 사람인/잡코리아를 통해서 구직을 하였지만 경력직 지원을 할 때는 원티드 라는 플랫폼을 이용하였다.

10개 정도의 회사에 서류를 지원하였고, 5개 회사에서 면접을 진행하였고, 3개의 기업에 최종 합격을 하였다.
그중 거리와 회사의 복지 등을 고려하여 하나의 기업을 선정하였고 이직을 하게 되었다.


이직을 위한 나의 노력

  • (1) 가고 싶은 회사 추리기
  • (2) 내가 갈 수 있는 회사 추리기
  • (3) 포트폴리오 만들기
  • (4) 개발자 이력서 작성하기

(1) 가고 싶은 회사 추리기

우선 목표를 정하는 게 중요하다. 내가 원하는 요구 조건이 분명 있을 것이다. 리스트를 작성해보고 해당 회사의 정보를 찾아 보자.

  • 연봉
  • 맥북지원
  • 소프트웨어 지원
  • 점심 식대지원
  • 개발환경
  • 도메인
  • 거리
  • 재택 가능여부 등

나의 경우는 다음과 같은 플랫폼을 이용 했다. 잡플래닛, 크레딧잡, 블라인드, thevc
스타트업에 지원한다면 최소한 기업 리뷰, 매출, 시리즈 투자 를 꼭 확인하자. 스타트업은 정말 어느 순간에(?) 망할지 모른다.

정말 이직을 하고 싶다면, 회사의 리뷰를 꼼꼼히 살펴보자!!

 

이렇게 나의 조건과 회사의 상황을 고려 했을때 대략 20개 정도 리스트가 나왔었다.

 

(2) 내가 갈 수 있는 회사 추리기

시간은 곧 이다. 연차를 막 쓸수 없기 때문에 내가 갈수 있는 회사 위주로 지원을 하는 것이 이득이다.
다음과 같은 상황에서는 지원을 하지 않는 것이 좋을 수 있다.

  • 1) '지원조건 Java/Spring 백엔드 개발 5년이상' 이지만 나의 경력은 3년도 안 되었을 때
  • 2) 'MSA 환경에서 서비스 개발 경력 3년 이상' 이지만 난 전무할 때
  • 3) '검색엔진 엘라스틱서치/루씬 개발 경력 3년 이상' 이지만 난 전무할 때

약간의 극단적인 예를 들었지만, 위의 예는 주니어를 뽑는 채용이 아닐 확률이 높다. 지원을 많이 해야 하는 것은 맞지만, 의미 없는 지원은 구직자나 채용 담당자 모두에게 에너지를 소모하는 일이라 생각한다.

채용의 상세 내용을 보면, 지원자격우대사항이 나뉘어져 있다. 일반적으로 지원자격은 회사에서 사용하고 있는 기술 스텍이고 우대사항은 회사에서 구축하고자 하는 내용이다. 따라서, 우대사항까진 아니지만 지원 자격까지는 맞추어야 서류의 합격률을 높일 수 있다.

ex) Java 개발 3년이상, Spirng Boot, JPA, ORM, AWS 환경에서 서비스 운영 경험

이러한 조건들을 꼼꼼히 살펴보고 리스트를 추려내 보았다. 대략 10군데 정도로 압축이 되었다.

 

(3) 포트폴리오 만들기

포트폴리오를 만드는 것은 필수는 아니다. 다만, 위에서 말한 지원 자격을 증명할 일이 없다면, 포트폴리오를 만들어서 증명을 해보자.


ex) Java 개발 3년이상, Spirng Boot, JPA, ORM, AWS 환경에서 서비스 운영 경험

SI를 위주로 일했던 나에게는 지원자격을 서류상으로 증명할 방법이 없었다. 따라서 해당하는 기술스텍을 포트폴리오로 만들었다. 사실 이 부분이 가장 시간이 많이 드는 부분이다. 그래도 이직을 위해서라면 3개월 정도는 투자해보자!!

(4) 개발자 이력서 작성하기

가능한 깔끔하고 담백하게 작성하자!!

  • 나의 기술 스텍
  • 포트폴리오(가능하면 git repository 첨부)
  • 회사에서 수행한 프로젝트
    • 맡은 역할, 수행한 일, 기술 스텍, 어려웠던 점, 팀의 기술 향상에 기여한 점
  • 학력사항 & 수상내역 & IT 관련자격증

내가 하지 않았던 경험은 쓰면 안된다. 이러한 부분은 면접에서 다 드러나기 마련이다. 내가 답변할 수 없는 내용들은 과감히 삭제하자.
이력서를 토대로 면접 질문들이 오갈 확률 들이 높기 때문에 키워드를 드러내자.

ex) 형상관리 시스템으로 git 도입, 배포시스템 구축, 모니터링 시스템 도입, ORM 도입, TDD 도입 등

면접의 경우 모든 질문에 답변을 할 필요는 없다. 다만, 면접을 보게 된다면 조금은 적극적으로 임해보자. 주니어에게 드라마틱한 롤을 기대하지는 않는다. 1인분을 할 수 있는 능력과 성장 가능성을 보고 채용을 진행하니 자신감 있게 면접에 임해 보도록 하자!!

 


읽은책 & 읽고 읽는 책 공유!!!

읽은 책

  • Just Java (강추!!)
  • 처음 배우는 스프링부트2 (강추!!!)
  • 실전 스프링 워크북
  • 스프링 철저 입문
  • 아마존 웹 서비스를 다루는 기술 (강추!!!!)
  • 자바 ORM 표준 JPA 프로그래밍 (강추!!!!!)
  • 무적의 글쓰기
  • 우리는 미래를 만든다

읽고 읽는 책

  • 엘라스틱서치 실무 가이드 (강추!!!!!)
  • 이펙티브 자바 3/e (강추!!!!!)
  • 모던 자바 인 액션 (강추!!!!!)
  • 스프링 마이크로서비스 공작소 (강추!!!)
  • 교양서적(?), 수필

2020년을 돌아보며

 2020년 상반기엔 이직을 준비하느라 쉴새 없이 바빴던 것 같다. Spring Boot, ORM 관련 도서를 읽고 포트폴리오를 만드느라 정신이 없었다. 바쁘고 정신없던 찰나에 나의 가능성을 좋게 봐주어 이직이 순조롭게 진행 되었던 것 같다. 하반기 역시 상반기와 마찬가지로 금방 지나갔다. 새로운 회사에서 적응 하는건 쉬운 일이 아니였다. 스타트업이다보니 개발부터 인프라까지 전 영역을 다루어야 했기 때문에 공부해야 할 것들이 너무나 많았다. 또한, 하반기엔 신규 서비스를 출시하느라 클라우드부터 배포 시스템까지 처음부터 다루다 보니 머리가 너무 아팠던 것 같다. 다행히 팀을 잘 만나서인지 순조롭게 서비스를 출시하고 지금은 잘 적응하여 순조롭게 회사에 안착할 수 있었다.

 

 2020년은 너무도 바빴던 시기였다. 작년엔 건강 관리에 조금 안일 했었는데, 올해 부터는 운동을 시작해 볼까 한다!! 물론, 1월을 기점으로 헬스를 등록하였다!!

 


2021년 목표

  • nextstep tdd 수강 (3월부터 ~5월 진행중!!)
  • 엘라스틱서치 실무 가이드, 이펙티브 자바 3/e, 모던 자바 인 액션, 스프링 마이크로서비스 공작소 완독!!
  • react 시작 & 토이프로젝트 진행하기
  • 건강챙기기 (헬스 주 3회 이상!!)
  • 알고리즘 leetcode로 공부하기

저도 어느덧 만3년이 지나고 4년차 개발자가 되었습니다. SI에서 오랜 시간을 보냈기에 2021년에는 코드의 품질을 향상 시키는데 초점을 맞춰 보려고 합니다!!

회고 끝~

블로그 이미지

사용자 yhmane

댓글을 달아 주세요

  • 2021.07.10 22:28  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • 사용자 yhmane 2021.07.10 23:07 신고  댓글주소  수정/삭제

      1. 포트폴리오를 개인으로 진행하셨는지 팀단위로 진행하셨는지 궁금합니다.

      팀단위로 진행하다가 혼자 진행하였습니다. 사실 이부분은 면접에 크게 중요치 않다고 생각합니다.

      다만, git branch 전략이나 깔끔한 commit 습관이 정착 되었는지 여부는 중요하다고 생각되네요.
      (git branch 전략, commit 컨벤션 )
      ------------------------------------------------------------------------------------------------------------

      2. 책을 통해서 필요한 기술스택의 지식을 모두 익힌 다음 프로젝트를 진행하셨는지,
      책을 통한 학습과 프로젝트 진행을 병행하셨는지 궁금합니다.

      저의 경우는 아래와 같이 진행하였습니다
      코딩 -> 버그, 이슈 직면 -> 웹서핑 -> 이슈해결
      + 시간날때 키워드를 검색하여 어떠한 것이 문제였는지 따로 정리

      책으로 학습을 하게 되면 좋지만, 사실 시간이 많지 않은 경우가 대부분이라고 생각하네요.
      우선적으로 마주친 문제에 대해서만 웹서핑을 하였습니다.
      이직후에는 실질적으로 다루는 경우가 많기에 책과 공식 문서를 이용해서 학습을 하고 있네요 ㅎㅎ
      ------------------------------------------------------------------------------------------------------------

      3. 포트폴리오를 만들면서 작성하신 코드에 대한 피드백은 어떤식으로 받으셨는지 궁금합니다.
      (멘토링 서비스를 따로 이용을 하셨는지, 커뮤니티 같은 곳에 공유를 해서 피드백을 받으셨는지 등등)

      피드백은 따로 받지 못했습니다 ㅠㅠ
      멘토링서비스는 아니지만 인프런 강의를 결제해서 들어보기도 하였고, next-step의 tdd 강의를 수강한 적이 있는데 주니어에게는 좋은 강의라 생각되네요! (광고는 아니고 개인적인 경험입니다)
      ------------------------------------------------------------------------------------------------------------
      4. 프로젝트를 진행하기 전에 참고를 한번 해보고 싶은데 만약 괜찮으시다면 포폴용으로 사용하신 리파지토리 링크를 공유해주실 수 있을지..부탁드려봅니다ㅠ

      si 시절에 짠 코드라 차마 공유 드리기가 ㅠㅠ..

      security, jpa, swagger 를 이용하여 간단한 backend-api 서비스를 구현하고 클라우드에 배포를 하였습니다.
      이동욱님의 spring-webservice 구현하기를 많이 참고하였던게 도움이 되었던 걸로 기억합니다!!
      http://www.yes24.com/Product/Goods/83849117
      ------------------------------------------------------------------------------------------------------------


      제 글이 도움이 되기를 바래봅니다 !!!
      준비 잘하셔서 이직 성공하세요!!!

  • 2021.07.14 07:59  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

직렬화

객체를 메모리, 데이터베이스, 혹인 파일로 저장하려면 어떻게 해야 할까? 이럴 때 필요한 것이 직렬화입니다.

  • 직렬화는 Serialization 이라고도 합니다.
  • 인스턴스의 상태를 그대로 저장하거나 네트웍으로 전상하고 이를 다시 역직렬화.복원(Deserialization) 하기도 합니다.

Serializable

  • 직렬화를 하려면 선언을 해주어야 합니다.
  • java.io.Serializable 인터페이스를 구현한 클래스의 객체만 직렬화 할 수 있습니다.
  • 직렬화 하지 말아야 할 값이 있다면 transient 키워드를 붙여줍니다.
    class Student implements Serializable {
    ...
    String name;
    transient String code;
    ...
    }
  • Serializable 인터페이스를 구현한 클래스는 자신의 serialVersionUID를 명시적으로 선언할 수 있습니다.
  • serialVersionUID는 필드의 이름이며 static, final이어야 하고 long type입니다. 선정하지 않으면 default 값이 선정됩니다.
    • class의 내용이 바뀌면 UID값 역시 바뀌게 되는데 네트웍 상황에서 값이 바뀌는 것을 방지하고자 사용합니다.

Serializable 사용해보기

class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private Integer grade;
    transient private Integer code;

    public Student(String name, Integer grade, Integer code) {
        this.name = name;
        this.grade = grade;
        this.code = code;
    }

    public String toString() {
        return "name : " + name + ", grade : " + grade + ", code : " + code;
    }
}

public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("홍길동", 2, 4885);
        Student student2 = new Student("아무개", 3, 7531);
        System.out.println(student1);
        System.out.println(student2);

        try (FileOutputStream fos = new FileOutputStream("test.dat");
            ObjectOutputStream oos = new ObjectOutputStream(fos);) {
            oos.writeObject(student1);
            oos.writeObject(student2);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try (FileInputStream fis = new FileInputStream("test.dat");
             ObjectInputStream ois = new ObjectInputStream(fis);) {
            Student stduent3 = (Student) ois.readObject();
            Student stduent4 = (Student) ois.readObject();
            System.out.println(stduent3);
            System.out.println(stduent4);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

결과

name : 홍길동, grade : 2, code : 4885
name : 아무개, grade : 3, code : 7531
name : 홍길동, grade : 2, code : null
name : 아무개, grade : 3, code : null
블로그 이미지

사용자 yhmane

댓글을 달아 주세요

문제


boj.kr/1463



풀이 & 코드

 

- 우선 어떤 유형의 문제인지 파악이 필요합니다

여기서는 동적 프로그래밍 (dynamic programming)의 문제입니다. 즉, 큰 문제를 작은 문제로 나누어 푸는 것 입니다.

작은 문제들이 반복되고, 작은 문제들의 값이 같을때 이를 동적 프로그래밍이라고 하는데 메모제이션 이라는 기법으로 문제를 접근합니다.


작은 문제들의 결과값은 항상 같기에 이 결과를 어떤 곳에 저장하여 놓는 것입니다.


- 이쯤 1463번 문제의 유형을 알았으니 동적프로그래밍의 해결방법을 찾습니다.

Bottom-up, Top-Down 방식이 있습니다. 어떤 것이 무조건 좋다는 답은 없고, 그때 그때 상황에 맞춰 적용하면 됩니다.


- 여기서는 Bottom-up 방식으로 풀었습니다.




/**
* dp
* bottom-up 방식
* 점화식 solve
*/

Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] dp = new int[n + 1];

dp[1] = 0;
for (int i = 2; i <= n; i++) {
    dp[i] = dp[i - 1] + 1;

    if (i % 3 == 0) { dp[i] = Math.min(dp[i], dp[i / 3] + 1); }

    if (i % 2 == 0) { dp[i] = Math.min(dp[i], dp[i / 2] + 1); }
}
System.out.println(dp[n]);

- 1) 3으로 나누어 떨어질 경우 : D[N] = D[N/3] + 1

- 2) 2로 나누어 떨어질 경우 : D[N] = D[N/2] + 1

- 3) 1 뺀다 : D[N] = D[N-1] + 1


다음 규칙의 최솟값을 메모제이션하여 결과값을 출력하여 줍니다.


블로그 이미지

사용자 yhmane

댓글을 달아 주세요

들어가며


  이번 포스팅에선 AWS 파일서버인 S3에 대하여 알아보고, S3의 생성, 연결 및 관리 방법에 대하여 알아보도록 하겠습니다.




S3란?

 

  S3란 Simple Storage Service의 약자입니다. 아마존에서 사용하는 파일 서버로 용량에 관계없이 파일을 저장할 수 잇고 웹에서 HTTP 프로토콜을 이용하여 파일에 접근할 수 있습니다. S3를 써야 하는 이유는 성능과 비용에 있습니다. 대용량의 파일 저장을 EC2와 EBS를 통해 구축한다면 상당히 많은 비용이 청구됩니다. (EC2는 RDS와 함께 AWS에서 많은 비용을 차지하는 부분중에 하나이기 때문에 EC2서버와 파일서버를 분리하는 것을 권장합니다) S3는 저장 용량이 무한대이고 파일저장에 최적화 되어 있습니다. 


 따라서, 동적 웹페이지를 EC2에 구축하고, 이미지 관련 정적 파일 등은 S3에 업로드 하여 구축합니다. S3는 흔히 웹하드와 비교하곤 하지만 HTTP를 이용한 파일 업로드/다운로드를 처리하기에 사용하기에 쉽습니다.


* S3 기본 개념


- 객체 (Object)

: S3에 데이터가 저장되는 최소 단위입니다. 객체는 파일과 메타데이터로 구성됩니다.

: 기본적으로 키(Key)가 객체의 이름이며 값(Value)이 객체의 데이터입니다.

: 객체 하나의 크기는 1 바이트부터 5TB까지 지원됩니다.


-  버킷(bucket)

: S3에서 생성할 수 있는 최상위 폴더입니다.

: 버킷은 리전(지역) 별로 생성해야 합니다. 버킷의 이름은 모든 S3 리전 중에서 유일해야 합니다

: 폴더 생성이 가능하고 버킷안에 객체가 저장됩니다.

: 저장 가능한 객체의 개수와 용량은 무제한입니다.

: 접속 제어 및 권한 관리가 가능합니다.


S3 버킷생성




먼저, S3 대쉬보드에서 버킷을 누른후 '버킷 만들기'를 클릭합니다.




 다음으로 버킷이름과 리전을 선택하여 줍니다. 리전은 지역 위치에 따라 속도가 차이가 많이 나므로, 운영할 서버 EC2에 위치한 리전으로 선택 해주는게 좋습니다. 버킷이름은 유니크 하기 때문에 이미 리전에 생성된 이름이 있으면 사용할 수 없습니다.



 생성된 버킷에 들어가면 다음과 디렉토리를 만들고 업로드, 다운로드를 대쉬보드내에서 진행 할 수 있습니다. 하지만, 이렇게 사용하려고 S3를 구성한 것은 아니니 코드(Java)로 제어하는 방법을 알아보도록 하겠습니다. Java 이외에 python, node.js, kotlin, .net 등의 언어로도 지원 가능합니다.



코드로 S3 다루기 feat Java

 

 먼저, S3를 다루는 몇가지 방식이 있습니다. 


1) IAM 계정 추가 방식

- 계정에는 access_key와 secret_key가 부여됩니다.

- s3 full access라는 정책을 계정에 부여합니다.

- s3 client에 access_key, secret_key를 부여한 credential 정보를 부여하고 bucket에 접근하여 업로드, 변경, 다운로드를 수행합니다.

AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

AmazonS3 s3 = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(region)
.build();


2) EC2에 역할 부여

- EC2에 S3에 대한 full access 정책을 할당합니다.

- s3 client를 할당하여 api를 통해 업로드, 변경, 다운로드를 수행합니다.

AmazonS3 s3 = AmazonS3ClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
엑세스 권한이 부여된 1) key 접근 방식은 보안적인 측면에서 여러 문제가 발생할 소지가 있기 때문에, AWS에서는 2번째 방법으로 S3의 파일 데이터를 다루는 것은 추천하고 있습니다!!

간단히 API를 보겠습니다.

- bucket_name : 버킷 이름 (유니크한 이름)
- key_name : 저장될 파일 이름 (해당 경로에 위치될 이름입니다)
- file : 파일

* upload 

try {
s3.putObject(bucket_name, key_name, new File(file_path));
} catch (AmazonServiceException e) {
System.err.println(e.getErrorMessage());
}

* download

try {
S3Object o = s3.getObject(bucket_name, key_name);
S3ObjectInputStream s3is = o.getObjectContent();
FileOutputStream fos = new FileOutputStream(new File(key_name));
byte[] read_buf = new byte[1024];
int read_len = 0;
while ((read_len = s3is.read(read_buf)) > 0) {
fos.write(read_buf, 0, read_len);
}
s3is.close();
fos.close();
} catch (AmazonServiceException e) {
System.err.println(e.getErrorMessage());
} catch (FileNotFoundException e) {
System.err.println(e.getMessage());
} catch (IOException e) {
System.err.println(e.getMessage());
}

* move, copy

try {
s3.copyObject(from_bucket, object_key, to_bucket, object_key);
} catch (AmazonServiceException e) {
System.err.println(e.getErrorMessage());
}

* delete

try {
s3.deleteObject(bucket_name, object_key);
} catch (AmazonServiceException e) {
System.err.println(e.getErrorMessage());
}


* 정리
s3 Object API 다루기 (bucket도  API 코드로 다룰수 있지만 혹시 모르니 버킷은 대시보드 내에서 작업 하시는걸 추천드립니다)
1) aws 클라우드 내 작업
- s3에 대한 엑세스 권한 부여

2) Java code 내 작업
- credential 부여 (1번, 2번 방법에 따라 optional)
- amazones3 client 주입
- api 수행


출처


AWS는 문서화가 잘 되어 있기에 공식 문서를 많이 찾아 보는걸 추천드립니다!!


AWS 공식 개발 document

AWS s3 Object Java API

블로그 이미지

사용자 yhmane

댓글을 달아 주세요

들어가며


 이전 포스팅에서 자바의 클래스에 대하여 알아보았습니다. 이번 포스팅에선 자바의 클래스에서 한단계 더 나아간 인터페이스와 추상클래스에 대하여 알아보도록 하겠습니다.


추상클래스와 인터페이스

 

인터페이스와 추상 클래스는 객체지향적 개념을 개발에 적용하며 설계를 확장하거나 향후 변경하기 쉬운 구조를 지원하는 요소입니다기본적인 속성과 필요한 메서드의 형태(프로토타입만) 기술하고세부적인 내용은 실제 구현 클래스에서 담당합니다.” 


 자바 실무에서 가장 많이 쓰이는 내용이 아닐까 라고 생각합니다. 공통적인 내용을 추상적으로 만들어 설계하는 곳에서 많이 사용됩니다. 실무에서는 보통 선임개발자가 추상클래스/인터페이스를 설계하여 공통된 로직을 적용하고 후임 개발자들이 내용들을 구현하는 용도로 많이 사용됩니다. 아래는 추상클래스와 인터페이스에 대한 내용을 간략히 표로 정리한 것입니다.


 

 추상 클래스

 인터페이스 

 상속 

 단일상속 

 다중상속 

 구현 

 extends 

 implements 

 추상메서드 

 0개 이상 

 모든 메서드 

 객체생성 

 생성불가 

 생성 불가 


  • 추상클래스

 추상클래스는 일반적인 클래스의 추상화 버전입니다. 추상클래스는 개념이나 사물에서 공통되는 특징이나 속성을 추출하여 설계하는 것입니다.

  • 인터페이스
 인터페이스는 추상클래스와 개념상 거의 동일하지만 아래와 같은 차이가 있습니다.

    • 인터페이스는 일반 메서드를 포함할 수 없으며, 모두 추상 메서드로만 구성합니다.
    • 일반 멤퍼 필드는 없고, public, static, final로 선언한 상수만 있습니다. (java8 부터는 생략 가능)
    • 다중 상속이 가능합니다.

추상클래스 예제

abstract class MyAbstractClass {
int num1, num2;
int result;

void calc() {
result = num1 + num2;
}

abstract int getResult();    // 추상메서드는 선언만
}

class MyClass extends MyAbstractClass  {    // extends로 상속 받음
int getResult() {
return result;
}
}

인터페이스 예제

Interface MyInterface {
public static final num = 1000;
public abstract int getResult();
}

class MyClass implements MyInterface {    // implements로 구현 받음
public int getResult() {
return num + 1000;
}
}



참조

- 자바의 정석

- Just 자바

블로그 이미지

사용자 yhmane

댓글을 달아 주세요

문제


boj.kr/10818



풀이 & 코드

 

값을 입력 받을때마다 최소/최대값과 비교하여 줍니다

int max = -1000001;
int min = 1000001;
for (int i = 0; i < cnt; i++) {
int num = scanner.nextInt();
if (max < num) {
max = num;
}
if (min > num) {
min = num;
}
}

System.out.println(min + " " + max);


블로그 이미지

사용자 yhmane

댓글을 달아 주세요

문제


boj.kr/10951



풀이 & 코드

 

입력이 없을때까지 체크하여 준다

public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

String input;
while ((input = br.readLine()) != null) {
String[] nums = input.split(" ");
System.out.println(Integer.parseInt(nums[0]) + Integer.parseInt(nums[1]));
}
}


블로그 이미지

사용자 yhmane

댓글을 달아 주세요