Develop/Kotlin

Kotlin Void vs Unit vs Nothing | Kotlin Void vs Unit vs Nothing

Void자바의 voidjava.lang 패키지안에 있는 Void 클래스 : java의 primitive type인 void를 래핑하는 객체이다. (int wrapper인 Integer과 같다고 보면 된다.)자바에서는 void 말고 Void를 리턴해야하는 경우가 많지 않다. : 제네릭에서 Void를 사용하는 정도의 용례package java.lang;/** * The {@code Void} class is an uninstantiable placeholder class to hold a * reference to the {@code Class} object representing the Java keyword * void. * * @author unascribed * @since 1.1 */publi..

Kotlin Void vs Unit vs Nothing | Kotlin Void vs Unit vs Nothing

728x90

Void

자바의 void

java.lang 패키지안에 있는 Void 클래스 : java의 primitive type인 void를 래핑하는 객체이다. (int wrapper인 Integer과 같다고 보면 된다.)

자바에서는 void 말고 Void를 리턴해야하는 경우가 많지 않다. : 제네릭에서 Void를 사용하는 정도의 용례

package java.lang;

/**
 * The {@code Void} class is an uninstantiable placeholder class to hold a
 * reference to the {@code Class} object representing the Java keyword
 * void.
 *
 * @author  unascribed
 * @since   1.1
 */
public final
class Void {

    /**
     * The {@code Class} object representing the pseudo-type corresponding to
     * the keyword {@code void}.
     */
    @SuppressWarnings("unchecked")
    public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");

    /*
     * The Void class cannot be instantiated.
     */
    private Void() {}
}

Void를 코틀린에서 사용할 때

fun returnTypeAsVoidAttempt1() : Void {
    println("Trying with Void return type")
}

이때 컴파일이 되지 않는다.

Error: Kotlin: A 'return' expression required in a function with a block body ('{...}')

그래서 코틀린에서 Void 객체를 만들어서 return 해야하나. 위의 Void 클래스 정의와 같이 private constructor로 인스턴트화가 막혀있다.

따라서 위 경우에는 어쩔수 없이 Void를 nullable로 만들고 Void? null을 리턴해야한다.

fun returnTypeAsVoidAttempt1() : Void? {
    println("Trying with Void return type")
    return null
}

작동하는 솔루션이 있긴하나. java의 void와 같이 동일한 결과를 낼 수 있는 방법으로 Unit 타입이 있다. (의미 있는 것을 반환하지 않는 함수의 반환 유형)

https://www.baeldung.com/kotlin/void-type

 

Unit

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/#unit

https://kotlinlang.org/docs/functions.html#unit-returning-functions

반환값이 필요없을 때, 함수의 반환타입으로 Unit을 사용한다.

반환타입이 Unit일 경우네는, return Unit;. return; 모두 선택적으로 작성해도 된다.

fun printHello(name: String?): Unit{
    if (name != null) {
        println("hello $name")
    } else {
        println("Hi")
    }
}

java에서의 void와 대응한다. 그러나 자바에서 void는 반환 값이 없음을 의미하는 특수 타입이지만, Unit은 class로 정의된 일반타입이다.

Unit은 기본 반환 유형이므로 그리고 return 타입 명시를 안했을 때도 함수가 작동한다.

// Unit.kt
package kotlin

/**
 * The type with only one value: the `Unit` object. This type corresponds to the `void` type in Java.
 */
public object Unit {
    override fun toString() = "kotlin.Unit"
}

따라서 Unit 타입을 반환하는 함수는 return을 생략해도 암묵적으로 Unit 타입 객체를 리턴한다 (싱글턴 객체이므로 객체 생성은 하지 않는다.)

즉 기원적으로 Void는 Java를 사용할 때 만들어진 클래스, Unit은 Kotlin을 사용할 때 만들어진 클래스인 듯

 

Nothing

kotlin에서는 throw가 expression 이다. 그래서 이때 throw의 타입이 Nothing 이다.

이 타입은 값이 없으며, 도달할 수 없는 코드 위치를 표한하는데 사용된다. Nothing을 사용하면, 도달할 수 없는 코드의 위치를 컴파일단에서 체크가 가능하다.

fun fail(message: String): Nothing {
    throw IllegalArgumentException(message)
}

컴파일단 체크 덕분에 잠재적인 버그와 좋지 않은 코드로부터 확인이 가능하다. 반환 유형이 Nothing인 함수가 호출되면 이 함수 호출 이상으로 실행되지 않고, 컴파일러에서 경고를 내보낸다.

fun invokeANothingOnlyFunction() {
    fail("nothing")
        println("hello") // Unreachable code
}

또한 Nothing은 type inference (타입추론)에도 사용이 가능하다.

  • null을 사용하여 초기화된 값일 때의 타입추론
  • 구체적인 타입을 결정하는데 사용할 수 없는 경우에서의 타입추론
val x = null // "type : Nothing?"
val l = listOf(null) // "type : List<Nothing?>"

Nothing은 java에서 대응되는 개념이 없으며, 자바에서는 주로 throw 처리를 할 때 void를 사용했었다.

// Nothing.kt
package kotlin

/**
 * Nothing has no instances. You can use Nothing to represent "a value that never exists": for example,
 * if a function has the return type of Nothing, it means that it never returns (always throws an exception).
 */
public class Nothing private constructor()

마찬가지로 Nothing도 객체를 생성할 수 없다. (값을 가지지 않는다.)

따라서 Nothing이 값을 가질 수 있는 경우는 Nothing? 에서 null 이 할당되었을 때 뿐이다.

Void

Java's void

The Void class in the java.lang package: It's an object that wraps Java's primitive type void. (Think of it like Integer being the wrapper for int.)

In Java, there aren't many cases where you need to return Void instead of void — it's mostly used in generics.

package java.lang;

/**
 * The {@code Void} class is an uninstantiable placeholder class to hold a
 * reference to the {@code Class} object representing the Java keyword
 * void.
 *
 * @author  unascribed
 * @since   1.1
 */
public final
class Void {

    /**
     * The {@code Class} object representing the pseudo-type corresponding to
     * the keyword {@code void}.
     */
    @SuppressWarnings("unchecked")
    public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");

    /*
     * The Void class cannot be instantiated.
     */
    private Void() {}
}

Using Void in Kotlin

fun returnTypeAsVoidAttempt1() : Void {
    println("Trying with Void return type")
}

This won't compile.

Error: Kotlin: A 'return' expression required in a function with a block body ('{...}')

So should we create a Void object and return it in Kotlin? Well, as you can see from the Void class definition above, instantiation is blocked by a private constructor.

So in this case, you have no choice but to make Void nullable as Void? and return null.

fun returnTypeAsVoidAttempt1() : Void? {
    println("Trying with Void return type")
    return null
}

While this is a working solution, there's a better way to achieve the same result as Java's void — the Unit type. (It's the return type for functions that don't return anything meaningful.)

https://www.baeldung.com/kotlin/void-type

 

Unit

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/#unit

https://kotlinlang.org/docs/functions.html#unit-returning-functions

When you don't need a return value, you use Unit as the function's return type.

When the return type is Unit, both return Unit; and return; are optional — you can write either or omit them entirely.

fun printHello(name: String?): Unit{
    if (name != null) {
        println("hello $name")
    } else {
        println("Hi")
    }
}

It corresponds to void in Java. However, while void in Java is a special type meaning "no return value," Unit is a regular type defined as a class.

Since Unit is the default return type, functions work even when you don't explicitly specify a return type.

// Unit.kt
package kotlin

/**
 * The type with only one value: the `Unit` object. This type corresponds to the `void` type in Java.
 */
public object Unit {
    override fun toString() = "kotlin.Unit"
}

So a function that returns Unit implicitly returns the Unit object even if you omit the return statement. (Since it's a singleton object, no new object is created.)

In other words, it seems like Void is a class that originated from Java, while Unit is a class that originated from Kotlin.

 

Nothing

In Kotlin, throw is an expression. And the type of that throw expression is Nothing.

This type has no value and is used to mark code locations that can never be reached. By using Nothing, unreachable code locations can be checked at compile time.

fun fail(message: String): Nothing {
    throw IllegalArgumentException(message)
}

Thanks to compile-time checks, you can catch potential bugs and bad code. When a function with a return type of Nothing is called, execution never continues beyond that function call, and the compiler emits a warning.

fun invokeANothingOnlyFunction() {
    fail("nothing")
        println("hello") // Unreachable code
}

Nothing can also be used in type inference.

  • Type inference for values initialized with null
  • Type inference when a concrete type cannot be determined
val x = null // "type : Nothing?"
val l = listOf(null) // "type : List<Nothing?>"

Nothing has no corresponding concept in Java. In Java, void was typically used when handling throw.

// Nothing.kt
package kotlin

/**
 * Nothing has no instances. You can use Nothing to represent "a value that never exists": for example,
 * if a function has the return type of Nothing, it means that it never returns (always throws an exception).
 */
public class Nothing private constructor()

Likewise, Nothing cannot be instantiated either. (It holds no value.)

Therefore, the only case where Nothing can hold a value is when null is assigned to Nothing?.

댓글

Comments

Daily/About Jyami

2021년 회고 | 2021 Year in Review

우선 회고를 시작하기 전에, “올해도” 연말에 카톡 메시징 서버 문제 없었다!!! 올해는 내가 직접 연말 대응을 핸들링 하게 되었어서, 연말 기록까지 남기고 싶어서 회고를 뒤로 미루게 되었다.20년도 9월에 입사를 하고 12월부터 실질적인 실무 업무를 시작하게 되었는데 22년이된 지금!! 내가 3년차라니?!! 너무 놀랍다ㅋㅋㅋ 사실상 실무를 1년 1개월 정도 한 상태인데 3년차라니.. 다시 한번 놀랍다.그래서 21년도는 나에게 입사 후 실무 개발자로써 적응을 하는 시기, 이제 개발을 직업으로 삼게 되면서 일과 일상의 밸런스를 위해 개발 외에 것들을 시도해보는 시기였다. 그렇다면 지금부터 회고를 시작해보자이번 회고는 1년치 일기장쓴 기분이라 가독성이 없다. 1. 회사1-1. 업무를 숙제처럼?매일매일 업무일..

2021년 회고 | 2021 Year in Review

728x90

우선 회고를 시작하기 전에, “올해도” 연말에 카톡 메시징 서버 문제 없었다!!! 올해는 내가 직접 연말 대응을 핸들링 하게 되었어서, 연말 기록까지 남기고 싶어서 회고를 뒤로 미루게 되었다.

재택근무 쾌-적-

20년도 9월에 입사를 하고 12월부터 실질적인 실무 업무를 시작하게 되었는데 22년이된 지금!! 내가 3년차라니?!! 너무 놀랍다ㅋㅋㅋ 사실상 실무를 1년 1개월 정도 한 상태인데 3년차라니.. 다시 한번 놀랍다.
그래서 21년도는 나에게 입사 후 실무 개발자로써 적응을 하는 시기, 이제 개발을 직업으로 삼게 되면서 일과 일상의 밸런스를 위해 개발 외에 것들을 시도해보는 시기였다. 그렇다면 지금부터 회고를 시작해보자

이번 회고는 1년치 일기장쓴 기분이라 가독성이 없다.

 

1. 회사

1-1. 업무를 숙제처럼?

매일매일 업무일지를 쓰면서 회사 일을 하나씩 처리했는데, 확실히 상반기와 하반기를 비교하면 많이 달라진게 느껴진다.
상반기만해도 일반채팅 개발버전 배포하는 것만해도 엄청 놀라워하고 가슴떨려하고 그랬는데, 하반기에 와서는 그 일반채팅을 리얼 버전으로 서버를 증설해서 배포하기도하고, 서버 배포 설정을 수정하려고 ansible을 이래저래 고치기도하고 결국 마지막엔 연말대응까지 이것이 짬이 차는 과정인가..?😳

빼곡하게 적었던 나의 업무일지 (내용은 비공개)

확실히 상반기 후반기 사이 내가 업무를 하는 모습에서 차이는 있었던 것같다. 상반기만해도 내가 이런 커다란 과제를 해도 될까? 잘 모르는데 메시징 일을 받아도 될까? 망설이면서 시니어분들이 지정해주시는 일만 했었다면, 하반기에는 상반기에 비해서 내가 먼저 일을 찾아서 했었던 것 같다. 파트에서 묵혀둔 이슈를 몇개 가져가기도하고, 클라이언트분과 친해지면서 적극적으로 커뮤니케이션하면서 메시징 로직 가이드를 드리기도하고, 코드에서 고치고 싶은 사항이 있다고 하면 먼저 이슈를 따서 진행하기도하고 그랬었다. 상반기 초에 파트 시니어 분께 “쟈미는 일을 숙제하듯이 해” 라는 말을 들었었는데, 이 말이 변하게 된 계기였던 것 같다. 처음 이 말을 들었을 때는 아무 생각없이 음 나는 숙제 항상 잘했고 퀄리티 높게 해왔었으니까. 되게 정성적으로 한다는 뜻인가? 하고 생각했었는데, 이 말이 신경쓰여서 은님께 여쭤보니, 일을 좀더 능동적으로 해야한다는 말이라는 것을 알게되었기 때문이다.

1-2. 어떤 개발자가 좋은 개발자일까?

그래도 아직은 이슈를 자잘자잘하게 가져가는 느낌이긴 하다. 자진해서 c > java 포팅작업을 주도하겠다 라는 시니어 개발자 분이 있었는데, 이분처럼 나중에는 정말 큰 이슈도 내가 먼저 이슈업 하는 날이 오지 않을까?라는 생각이 들기도한다. 이런 걸 보면 확실히 메시징파트에는 참 배울 분들이 많다고 느낀다. 그런데 그때문에 입사 전과 후로 어떤 개발자가 되어야하나라는 생각에 변동을 겪고있다. 이전에는 당연히 자바, 스프링 좋아하니까 그걸 마구마구 파고 막 클린코드, DDD이런거 다 적용하는 개발자가 좋고, 당연히 실무에 들어가면 spring구조 다 까보고 그러고 있으면 좋은 개발자가 되지 않을까? 라고 생각하고 있었다, 그런데 입사 이후에는 어떻게 하면 좀 더 좋은 구조의 서버 구조를 가지고, 여러가지 컴포넌트를 올바르게 사용할 수 있는지도 매우 중요하구나를 깨닫고있다.

왜냐하면 아무래도 장애가 나거나, 새로운 컴포넌트에 대한 계획을 세울때는 코드를 어떤방식으로 짜고.. 보다는 어떻게 하면 좀더 성능을 높일 수 있고, 이런 엣지케이스에는 어떻게 대응할 것이고 등등의 실무적인 관점을 접했기 때문이 아닐까 싶다. 그래서 이전에는 redis를 사용한다하면, spring-data-redis를 사용할 때 어떤 메서드를 사용해야 좋은 코드 좋은 테스트 코드를 짤 수 있다! 이런거를 고려했다면, 지금 메시징 파트에 와서는 redis 자체에 대해서 고민을 해야한다는걸 깨달았다. redis가 가지는 자료구조, failover 정책, evction 정책, 백업 정책 등등 그 자체에 대해서 먼저 알고가는게 더 중요할 수 있다는 걸 경험적으로 습득하게 되었다. 그래서 그냥 어떤 언어를 잘하는 보다는 문제상황이 발생하면 잘 해결하는 개발자가 사실 일하기는 가장 좋은게 아닐까 하면서 생각이 변하는 중이다. 파트장님과도 여러번 고민을 공유했었는데, 다 다른길을 걷고있고 어떤 개발자가 좋다는 정답은 없다고 조언을 주셨었다.

1-3. 인턴멘토 / 공채 신입 코드리뷰어

그리고 하반기부터 내 아래로 인턴 / 신입분들이 들어왔다. 나는 내가 아직도 신입같은데.. 내가 인턴분의 멘토를 했었고, 공채 신입 분들의 코드리뷰를 담당하고 있다. 사실 처음에는 나보다 나이가 많으신 분들을 대상으로 괜찮으려나 걱정도 했었는데 괜찮았다.✌️ 어찌됐든 신입 과제가 내가 알고있는 선에서 나왔고 모르면 공부하면 되니까라고 생각했다.
나도 연차가 많이 높진 않지만 인턴 멘토를 하면서 회사가 어떤 사람을 신입개발자로 뽑고 싶어하는지를 파악하면서 가이드를 드렸었다.

더보기
많은 신입개발자분들이 과제를 하다보면, 정말 실무자처럼 잘하고 싶어하고 물어보지 않아도 본인이 척척 다 하는 모습을 보여주고 싶어한다. 그래서 멘토가 준 요구사항 명세를 꼬치꼬치 캐묻고, 그러다보면 정량적으로 요구사항을 충족하게 된다. 그런데 그러다보면 놓치기 쉬운 것이 “왜 이렇게 개발을 해야겠다고 결정했는가, 왜 이런 결과물을 내야겠다고 생각했는가”라는 왜라는 질문이다. 서버 성능 테스트라는 요구사항이 있다고 해보자. 신입은 그 요구사항을 보고, 서버 성능테스트에 대해 찾아보고 멘토에게 ngrinder라는 툴을 추천받아서 서버의 tps가 잘 나온다는 것을 확인해서 결과 보고서로 낸다. 그런데 최종 면접관은 이렇게 물어본다. “왜 서버 성능테스트의 비교 대조군을 이렇게 했어요?”, “만약 N명의 유저가 접근해도 문제가 없으려면 이 서버 tps로는 부족할 거 같은데 혹시 다른 지표가 있을까요? / 어떻게 개선이 가능할까요” 등등…의 질문을 유도한다. 왜 이런 코드를 짰는지, 이런 툴을 왜 썼는지, 왜 이런 구조로 서버가 되어있는지 의사결정을 하게된 논리적인 흐름을 중요시한다.

근데 이런 질문리스트까지 멘토가 미리알고 하나하나 신경써주고 대비시켜준다는건 사실 쉽지않다고 생각한다. 따라서 본인이 여러 참고자료를 찾아보고 이툴과 다른 툴과의 비교해서의 장점부터 시작해서, 현재 본인이 활동중인 파트에서는 어떤방식으로 사용되고있는지 멘토한테 조언을 얻고, 인턴과제로 구현을 하지 않더라도 확장계획이나 현재 부족한 점들을 미리 알고 대비하는 그런 열정있는 모습과 성장가능성을 회사는 보고싶어하는 것 같다. 이렇게 써놓고 보니 쉽지않다…😳


인턴분의 근처에 가장 가까이서 일하면서 많은 것들을 느끼게 되었고, 최대한 합격을 하는데 도움이 되도록 코드단부터 의사결정단까지 조언을 열심히 해주었다. 그러나 당시 인턴분보다 나의 열정이 더 과했던거 같았다..하핫 그래서 사람과의 협업과 매니징이 쉽지않다고 이 당시에 느꼈던게 가장 생생하고, 개발자로서 이 시기에 조금 고민이 있던 것 같다. 앞으로는 많은 사람들의 선배개발자가 될텐데 어떤방식으로 조언을 드려야 도움이 될까를 고민해보게 되었다. 그래서 요즘 신입분들 코드리뷰하면서, 우선 코드단부터 읽으면 좋은 책과 아티클을 PR에 던져드리고 있다🙌 (리뷰를 좀 빡세게 하는 편이라.. 제 리뷰이분들 화이팅..ㅎ)

근데또 써놓고 보니 어렵다 ~_~ 원래 인생 계획없이 사는 타입이라 (지독한 ESTP) 대충 현생에 충실하다보면 미래에도 꽤 괜찮은 사람이 되어있지 않을까

2. 개발 외부 활동

올해에는 커뮤니티 활동을 줄였다. GDSC EWHA 리드였다보니 카카오 면접볼때 받은 질문이 “개발커뮤니티 하는거 좋아하시나요?” 였는데, 그 답변을 충실히 수행했다. “3-2학기 리드활동에 몰입하면서 많은 사람들에게 기여했다는 뿌듯함과 새로운 경력을 쌓은건 좋았다. 그러나 그 때는 개발자로서의 성장이 멈춘 학기였다. 연사섭외 행사준비 등등을 하다보니 개발 공부를 할 시간이 부족했고, 3-1학기의 제 개발 실력과 거의 비슷해서 현타가 왔었다. 그래서 회사에 입사한다면 초반에는 개발공부와 회사 적응에 집중하기 위해 개발커뮤니티쪽에서 눈을 조금 돌리 생각이다.” 라는 답변이었다. 그대로 실천했다. 원래 뭐를 하자는 결심보단 뭐를 하지말자라는 결심이 더 편해서😝

근데 또 막상 되돌아보니 이것저것 하긴 했었더라..? 어쩔 수 없는 관종인가 싶기도하다.

2-1. 유튜브

유튜브는 한달에 한번을 목표로. 블로그는 최대한 기술적인 것들 위주로 쓰고 싶었는데 부진했다. 뭐 어쩔수 없지, 현생을 살면서도 이만큼 챙긴게 어딘가!! 흠흠 유튜브는 (22년 1월 16일 기준) 2813명의 구독자가 생겼다. 정말 소소한 VLOG 동영상만 올리는데도 다들 좋아해주셔서 신기하다. 정작 나는 유튜브는 정말 킬링타임용 및 힐링용으로 예능(더지니어스, 크라임씬, 워크맨 , 문명특급), 게임(파카, 쌀튜브, 여왕럭스) 이런류만 보는편이라 나는 브이로그를 안보는 유튜버다..! (당당)🙄
회사분들이 유튜브 수익 물어보시는데 없다. 유튜브 수익을 얻으려면 구독자 1000명이상 시청시간 4000시간 이상이어야하는데, 내가 영상을 많이 올리지 않아서 시청시간이 부족해서 광고수익이 없다. 아 그런데 상반기에는 멋쟁이사자처럼, 마스슬립에서 협찬 및 광고 제안을 해주셔서 관련 영상을 찍었었는데, 이렇게 협찬을 받고 제작하는 영상은 뭔가 일이 되는 느낌이라 유튜브 영상 올리는게 재미가 없어져서 그 이후부터는 모든 협찬을 거절하고있다. 개발 관련 제품 요청이 아니면 앞으로도 화장품이나 등등의 잡화 요청은 거절할 것 같다ㅋㅋ (뭔가 전자제품이나 파우치 이런거는 유용하지 않을까 싶기도🤔)

유튜브 스튜디오를 보면 분석 전략이 나오는데, 여기 보면의외로 코드윗미가 재생이 많이된다. 그래서 뭔가 나도 다른 코드윗미처럼 이쁜 인테리어 이쁜 조명으로 올리고싶은데..! 음 나는 인테리어가 그렇게 어렵더라;;; 뭔가 내가하는 인테리어는 감성이 부족함 좀더 연구해보겠다😵‍💫 시청자 층은 아무래도 개발자들이 많이 보는지 연령으로는 18~34세가 성별으로는 2:1(남:여) 이더라. 판교뚜벅초(아 진짜 좋아하는 유튜버다 영상이 너무 재밌음), 조코딩, 노마드 코더 유튜브로 흘러들어오는 사람들이 많은듯.

2-2. 블로그

블로그는 사실 올 한해 책을 읽은 달이 훨씬 더 많은데, 책내용을 정리해두긴 했으나 뭔가 내가 정리해둔 정리본을 다시 보기다는 그냥 한번더 읽지 라는 생각이 요즘 들어서 깃에만 저장해두고 블로그에는 따로 정리해서 안올렸다. 그러다보니 블로그에 올라오는 글이 현저히 줄어들었다. 그래서 블로그 인기 글이 대부분 회고록/ 취뽀기 인데,, 흐음 기술 글보다 회고록이 인기가 많은 개발자 블로그라니 🙄이게 맞나? 파트에서 일하면서 ansible이나 이것저것 정리해보고 싶은 주제가 몇개가 있는데 아 주말에 블로그 쓰기/ 유튜브 편집하기가 정말 쉽지않더라 직장인에게 꿀같은 주말이기 때문에…😘 22년에는 시간을 좀 내보는걸로 하자.

아근데 생각해보니, 내 블로그가 아니라 회사블로그에 글을 썼다.ㅋㅋㅋㅋ 카카오 테크 블로그에 내 글이 있다는게 뿌듯하기도하고.. 한번쯤은 해보고 싶었는데 유관부서에서 먼저 요청이 왔다 하핫. 글은 아래에 있다.

https://tech.kakao.com/2021/05/24/jyami/

 

뉴크루의 카카오 백엔드 개발자 이야기

안녕하세요, 톡플랫폼개발팀 톡메시징파트의 신입 개발자 쟈미(jyami)입니다. 저는 자바, 코틀린을 좋아하는 백엔드 개발자이고, 카카오에 들어온 지는 약 반 년이 되어갑니다. 제가 카카오에 입

tech.kakao.com


https://tech.kakao.com/2021/10/25/grace-hopper-celebration/

 

Grace Hopper Celebration 참가기

카카오에서는 크루의 주도적인 성장과 역량 향상을 위해 글로벌한 인사이트 확대를 경험할 수 있도록 해외 컨퍼런스 참관 참여를 지원하고 있습니다. 안녕하세요, 톡플랫폼개발1팀의 jyami입니

tech.kakao.com


위에있는 뉴크루의 백엔드 개발자 이야기가 당시에 페북에서 공유가 엄청 많이 되었던 기억이 있다. 확실히 취업/신입 개발자 관련 주제는 엄청 핫한가 보다. 다음에 회사블로그에 또 글을 쓴다면 그건 꼭 기술에 대한 적용기 이야기를 쓰고 싶다. 그동안 너무 커리어적인 글만 양산한거 아닌가 싶어서 내가 과연 기술적으로 성장하였는가? 라는 고민이 있었기 때문이다.🙌 (근데 요즘 공부를 안함…흠…)

2-3. 외부 인터뷰

상반기에 이곳저곳에서 인터뷰가 생각보다 많이 들어왔었다. 다 비슷비슷하게 취업기 관련 커리어 글이었는데, 학교에서도 들어오고 대학 잡지, 기술 뉴스레터 등 다양한 곳에서 나의 이야기를 담아주셨다.

http://inews.ewha.ac.kr/news/articleView.html?idxno=32531

 

선배들의 따끈따끈 ‘취뽀’ 성공담 – 카카오(Kakao) 개발자 편 - 이대학보

코로나19의 여파로 취업 시장이 얼어붙었다. 청년층의 실업자 수는 2017년 이후 최고치를 기록했다. 이런 취업난에도 최근 다양한 분야에 취뽀(취업뽀개기)한 이화인들이 있다. 본지는 이들의 이

inews.ewha.ac.kr

https://maily.so/1step/posts/4a440c

 

📑 IT 대기업 입사는 공채만이 답?

프로합격러가 들려주는 주니어 상시채용 취준 꿀팁

maily.so

http://www.campl.co.kr/contents/content_read.asp?idx=10483

 

“하고 싶은 건 다 해보고, 끊임없이 배우는 것이 중요해요” 카카오톡 김민정

캠퍼스플러스

campl.co.kr

 

3. 스터디

신입일 때는 이것저것 막 공부해야하는거 아닌가 싶어서 상반기 하반기 모두 채찍질 하면서 스터디를 생각보다 많이 했던 것 같다. 그런데 지금 되돌아보면 이것저것 일을 벌리면서 스터디를 하는 것 보다 정말 관심있는 것 몇가지만 집중해서 하면 좋았을텐데라는 아쉬움이 있다. 현재는 스터디를 이것저것 했지만, 그 시간을 헛으로 보낸 것 같다는 느낌이 들어서 전부다 그만두고 쉬고있는 상황이다. 내가 공부하고 싶을 때 하고, 스터디를 시작한다면 상황적으로 여유가 될 때 하나만 집중해서 하고싶다.

3-1. Next Step Effective Kotlin (2월 ~ 3월)

혼자서 코틀린 인액션 책을 읽었는데 챕터 5까지 정도밖에 안읽고 실제로 쓸 일이 없으니까 코틀린을 적당히 자바 문법이랑 호환되는 부분만 사용할 줄 알지, 실제 코틀린 문법을 알고 적절하게 사용하고있다는 느낌이 안들었다. 그래서 자바봄에서, 우아한 프리코스에서 했던대로 코틀린을 써보면 좀 더 익숙해지지 않을까 생각해서 넥스트스텝 교육을 신청했다. 리뷰를 받으면서 내가 몰랐던 코틀린 문법이나 패턴들을 알게된 건 좋았으나, 퇴근하고 미션을 수행하는게 정말 쉽지않더라.. 그래서 미션 4개중에 2~3개 정도까지만하고 완수는 못했다. 그래도 리뷰 받은 내용들이 모두 도움이되는 내용이었고, 감사했다. 따로 다시 공부해야하는데… 이번에 코틀린 관련 업무를 시작하면서 좀 더 봐야겠다.
https://edu.nextstep.camp/c/Z9QeJlCi/

 

이펙티브 코틀린(Effective Kotlin) with TDD, Refactoring, Clean Code

edu.nextstep.camp


3-2. 자바봄

대학교 3학년때부터 하던 스터디이다. 사실 이 스터디 덕분에 자바를 좀더 집중적으로 공부할 수 있었고, 취업할 때 덕이 컸다고 생각한다. 모든 인원이 전부 취업에 성공하면서 배민2 / 네이버 / 카카오 / 마리트 이렇게 모두 직장을 갖게되었는데, 그럼에도 나름 꾸준히 올해도 책을 조금씩 읽어나가면서 서로 질문하고 답변하고 찾아보는 형식의 스터디를 계속해서 진행했다. 자바봄에서 올해 했던 책 리스트에 대한 건 아래 링크에서 볼 수 있는데, 어느 순간 이 리포가 스타 200을 넘어섰다!! 다들 열심히 정리하고 관리하는 레포지토리라서 많은 사람들이 참고한다는 점에 되게 뿌듯하고 신기해했던 기억이 있다.
https://github.com/Java-Bom/ReadingRecord

 

GitHub - Java-Bom/ReadingRecord: 📚 책 읽고 정리하기 📚

📚 책 읽고 정리하기 📚. Contribute to Java-Bom/ReadingRecord development by creating an account on GitHub.

github.com

자바봄에서는 총 3가지 책을 했는데, 각 책에 대한 느낌은 아래와 같다. (괄호에 들어간 이름으로 issue 라벨을 관리중이다.)

  • 토비의 스프링 (Spring of Toby) : 확실히 스프링을 하는 개발자라면 한번은 읽어 봐야하는 책이었다. 대충 읽고 넘어간 부분도 있고, 집중해서 읽고 넘어간 부분도 있었는데, 이 책을 다시본다면 그때는 스프링 내부 코드와 함께 봐야겠다는 생각이 들었다. 다만 이번에 책을 읽을 때는 호흡을 길게 가져가면서 읽어서 이전에 읽었던 내용도 까먹고 넘어가고 했던게 종종 있었다. 앞으로는 두꺼운 책이라도 호흡을 짧게 가져가면서 읽고싶다. (쉽지않겠지만..)
  • 쿠버네티스 입문 (Kubernetes introduction) : 음 개인적으로 재미없었다. 이책 읽은 것중에 쿠버네티스 구조 부분 좀더 보고싶어서 쿠버네티스 인액션이랑 공식문서 함께 보면서 혼자 정리한 부분은 재밌었는데, 그 외에는 yml 다 따라치면서 어떤 기능이 있다 소개하는 거였기 때문이다. 나는 아무래도 이미 쿠버네티스를 사용해본적이 있다보니 yml 작성하면서 각 오브젝트의 기능을 소개하는 부분은 이미 알고있는 내용이라 그랬을수도 있다. 그래서 쿠버네티스 입문 책보다는 이후에 읽은 쿠버네티스 인액션 책이 좀 더 재밌었다. 그치만 쿠버네티스를 배워본적이 없어서 핸즈온랩처럼 따라하면서 가고싶다면 이책도 나쁘지 않은듯?
  • 스프링 배치 완벽 가이드 (Definitive Guide to Spring Batch) : 이 책을 읽으면 좀 더 스프링 배치를 잘 짤 수 있지 않을까 라는 기대로 시작했는데, 정작 소득이었던건 배치 repository 구조를 좀 보게된 것 정도인 것 같다. 내가 리딩을 하지 않으면 적당히 책을 읽고 넘어갔었기 때문이다. (이때 쯤부터 책읽는걸 조금 힘들어했었다.) 배치 인터페이스를 소개하면서 각종 구현체들에 대한 설명까지 간략하게 나와있는데, 이런게 있구나 하고 알고 넘어가고 사용할 때 직접 써보지 않는다면 잘 와닿지 않는다는 느낌이 있어서 그랬던 것 같다. 역시 책만 읽기보다는 직접 코딩 해보면서 감을 잡아보는걸 좋아하는 타입이다. 그러나 사용하고 싶은 구현체가 있다면 이 책으로 간단히 개요를 보고, 스프링 배치 공식문서를 본다면 빠르게 파악하고 넘어갈 수 있을것 같다.

지금은 자바봄 스터디를 멈춘 상태이다. 다들 일이 바쁘고, 의욕이 안살아서 조금 휴식기를 가지기로했다.

3-3. 설계 스터디 (8월 ~ 11월)

  • 가상 면접 사례로 배우는 대규모 시스템 설계 기초

GDSC로 알게된 정동오빠가 주도해서 하게된 스터디이다. 아예 인연이 없는 새로운 개발자 분들과 스터디를 하게 되었고, 배민 / 토스 / AWS / 카카오 조합으로 책을 봤다. 다양한 회사의 사람들이랑 이 책을 읽었을 때 좋았던 점은 각 회사 본인의 도메인에서 봤던 설계에 대해서, 실제로 어떻게 사용하고있는지 경험담을 들을 수 있던게 좋았다. 이 책 내용이 조금 대략적으로 간단하게 설계에 대한 이야기를 하고있는데, 책의 내용으로는 가벼울 수 있는 내용을 각 구성원들의 경험과 지식으로 채우면서 생각보다 재밌었다. 이 스터디를 하면서 위에 말했던 애플리케이션 코드의 중요성 뿐만아니라 설계에 대한부분도 좀더 보는 개발자가 되어야하지 않을까? 라는 생각을 하게되었다.

정동오빠가 설계 관련해서 공부하기 좋은 아티클 링크나, 유튜브 영상 링크를 보내줬는데, 올해중에 맘잡고 전부 싹 훑어보고 블로그에 하나씩 업로드하는게 목표이다. 이 책을 읽은 것 만으로는 조금 빈약한 부분이 있었기 때문이다. 그리고 이 책을 읽으면서 면접에서 서버당 TPS 이런 수치들은 어떻게 추측할 수 있는지 우리파트 시니어 개발자 분께 여쭤봤는데 경험의 영역이라고 한다. 그래서 파트에서 사용하고있는 서버의 대수나 그에 대비하는 TPS, 구조를 좀 더 기억하거나, 정리하는게 중요하다는 인사이트를 받을 수 있었다. 실제로 파트에서 장애나, 연말대응같은 빅 이벤트에서의 회고를 진행하면서 여러 수치적인 지표를 한번 더 짚고 넘어가고 있는데, 할 때마다 되게 좋긴하다.


3-4. 책 스터디 (1월 ~11월)

GDSC로 알게된 원범오빠가 본인의 대학 친구들하고 하고있는 스터디이다. 왜하게된건지 기억은 안나는데, 이 스터디는 자바봄과는 다르게 엄청나게 짧은 주기로 책을 읽고 리뷰한다. 책 한권을 잡으면 일주일에 2번 모이고, 각 모일때마다 한챕터씩 읽어야한다 (약 60~70페이지 분량). 기존에 책을 널널하게 읽는데에 익숙해져있어서, 하루에 한 챕터 읽기도 힘들어했던 내가 (소설아니면 책읽는거 진짜 싫어한다 원래). 그냥 하루에 열심히 한 챕터 정도는 읽어보자하면서 변화하게 되었다. 그러다보니 생각보다 많은 책을 읽을 수 있다는 점은 좋았지만, 집중해서 보고싶은 부분을 시간에 쫓겨서 넘어간 적도 많았었다.

  • 마이크로 서비스 패턴 : 가장 맨 처음에 읽었던 책이라서 기억이 잘 안난다ㅠㅠ 그런데 되게 여러가지 패턴이나 도식도를 보면서 최대한 내가 알고있는 예제들과 적용시키면서 이해하려고 했던 기억이 난다. 아무래도 파트에서 이미 적용하고 있는 부분도 있고, 아닌 부분도 있어서 이런 이해 방식이 가능했던 것 같다. 가장 어려웠던 부분은 아무래도 분산 트랜잭션 부분이었다.
  • DDD Start : DDD를 모르는 스터디 원들도 계셨기에 앞에있는 MSA 책 이전에 이 책을 읽으면 좋았겠다 싶었다. 좋았던건 내가 그동안 객체지향이라고 부르던 것들이 애그리거트 / 도메인 / 도메인 모델 등등 특정 단어로 명명되고 있다는 걸 알게되었다는 점이었다. 이 책을 읽기는 했지만 나는 DDD를 하고있는가? 라고 제대로 알고 사용하고 있는 것 같지는 않다고 대답할 것 같다. 위에서 말한 용어들을 의식적로 인지하고 구현하고 있지 않기 때문이다. 책 자체는 엄청 쉽게 설명이 되어있어서 말 그대로 DDD를 시작하는 사람이 읽으면 좋을 것 같다.
  • 리팩터링 (2판 사진을 올렸으나 자바로 되어있는 1판을 읽었다.) : 파트에서 리팩터링 과제를 맡고있어서 이 책을 읽으면 엄청 도움이 되지 않을까? 기대를 하고 봤는데 사실 조금 실망인 부분도 있었다. 여기서 소개하는 일부 패턴 및 전략들이 intellij ide로 대체가 가능 한 것들이 있었는데, 옛날 책이라 어쩔 수 없는 것 같다. 그러나 개발자 교양서라고 부르는건 인정.
  • 쿠버네티스인액션 : 쿠버네티스 yml을 짜기는 하지만, 좀더 깊게, 혹은 구체적으로 알지 못했던 부분을 하나씩 추가하는 느낌이라 좋았었다. 그러나 뒷부분으로 갈 수록 사용을 안해본 오브젝트이거나 하는 경우에는 이해나 응용을 떠오르기 어려웠었다. 근데 etcd, api 서버, kubelet 등 쿠버네티스 구조와 관련된 컴포넌트들을 이 책을 통해 열심히 봤었다.
  • 데이터중심 애플리케이션 설계 : 아 진짜 어려웠다. 이 책이랑 대규모 시스템설계 기초 책을 함께 봤었는데, 그러다보니 더더욱 분산 시스템 설계의 중요성에 대해서 공부해봐야겠다고 생각이 들었던 때가 이 때쯤이었다. 아근데 진짜 어렵다. 책의 모든 내용이 나에게 도움이 되진 않았고, 한 앞에서부터 2/3 지점까지가 굉장히 좋았다. 뒷부분은 하둡, 스파크 등 데이터 분석에 대한 내용이었는데, 아무래도 관심이 가질 않았기 때문이다.

이 스터디역시 마무리했다. 올해 스터디를 전부다 책 스터디만 하면서 스터디 시간으로 인해 책만 읽고 실제 코드로 구현을 해보거나 더 찾아본다거나 하는 일이 없었어서 조금 현타가 왔었다. 그래서 지금은 전부다 그만둔 상태! 리프레시 하고 다시 원하는 공부를 하는걸로 하자.

3-5. 사내 스터디

  • redis 스터디 : 사내 스터디로 파트 대부분의 분들이 참여했었는데, 여기에서도 redis-cluster를 k8s helm과 함께 사용하는 방법에 대한 발표를 내부적으로 진행했었다. 이때 bitnami 레포를 분석하면서 분석한 내용을 바탕으로 내부적으로 어떤 명령어들이 실행되어서 이런 설정들이 가능한지를 발표했었다. 그러나 현재 파트에서 진행하는 redis-cluster 전환 작업은 참여하고있지는 않다. 전환 작업을 하고계시는 분들의 진행과정 발표를 들었었는데, 공부한 내용을 직접 적용하기 위해 많은 것을 공부하고 설정값 확인부터 모니터링까지 모든 것을 염두하기 쉽지않을텐데 정말 대단하시다고 생각했다.
  • UNIX 고급프로그래밍 : 내가 입사하기 9개월전에 입사하신 주니어 개발자 분과 둘이서 하고있다. 아무래도 우리 파트 코드중에 C로 되어있는 코드가 있어서 이 책을 읽으면 조금 진입장벽이 낮아지진 않을까?! 기대를하면서 이책을 시작하게 되었는데. 엄.. 어렵다. unix에서 사용하는 함수나 용법에 대한 설명이 있어서 와닿기 어려울 때도 있고, 어쩔때는 운영체제 수업, 컴퓨터 구조 수업을 잘 들어둘껄 생각도 든다. 그래도 회사 분이랑 함께 시작 한 스터디니까, 어찌저찌 끝마추지 않을까


3-6. 외에 개인적으로 본 책

  • 네티 인액션 : 이전에 내가 검증형계약직에서 정규직으로 전환될 때 했던 과제가 네티가 필요해서 읽었었다. 그런데 인턴분이 진행하신 프로젝트 역시 네티를 사용해서 코드리뷰와 적절한 가이드를 위해 한번 더 이 책을 읽었다. 한번 읽으면 슈루룩 읽는데 뭔가 응용해서 짜보려하니 쉽지 않았다.
  • 코틀린 인액션 : 읽다가 말았다ㅠ 책을 읽는거에 그치지 않고 책에 있는 예제를 하나하나 따라해보고 실행해보아야 온전한 내 것이 될 것 같아서 눈으로 읽기를 그만두었으나. 다른 책 스터디에 밀려서 우선순위가 후순위가 되었다.
  • redis 운영 관리 : 간단하게 읽기 좋은데 진짜 꿀팁만 담겨져 있다. 100페이지도 안되서 더 좋다. 간략 정리글은 내 블로그에 있다 : https://jyami.tistory.com/141
  • 프로그래머의 뇌 : 요즘 읽고있다. 개발 책만 읽는게 힘들어서 개발 인문학(?) 느낌으로. 근데 읽다보니 내 뇌가 마치 컴퓨터처럼 되어있나라는 생각이 든다. 사람의 기억을 램과 디스크와 CPU로 비유해서 서술한다.

 

4. 이화여대

4-1. 토익과 졸업

졸업했다!!! 드디어!! 토익 졸업 자격 요건 때문에 졸업을 못할 뻔 했는데 이것도 통과했다!!! 사실 원래대로라면 20년 말에 토익을 보고 21년 2월 졸업을해서 바로 칼졸업을 하는게 베스트였는데, 20년 말에 카카오 정규직 전환 인터뷰를 준비를 하면서 학교 졸업요건들을 잘 챙기지 못했었다. 그래서 유예하고 21년 8월 졸업을 노렸었는데.. 이것 역시 까먹고 얼레벌레 살다가 한 6월쯤 되서 생각나서 토익 점수 마감일 계산해서 공부를 딱 2주 할 수 있는 시간이 있었다.
원래 20년 말에 노베이스로 토익을 그냥 공부 안한 상태로 봤을 때 565점이었다. 그런데 졸업을 위해 700점을 2주만에 맞춰야하는 상황이었다..!! 미리 공부좀 해두었으면 좋았을걸, 나중에 나중에 미루다가 2주동안 공부해서 135점을 올려야하는 상황이었다. 그래서 당시에는 9시 출근 6시나 7시 퇴근하고 밥 먹고 10시 11시까지는 토익공부에 집중하고 힐링을 위해 롤 1~2시간 정도 하는 삶을 살았었다. 그렇게 해서 결국 나온 결과는 710점. 영어공부 목적이 아니라 정말 졸업만을 생각하고 달렸었기 때문에, 700점만 넘으면 되서 더도말고 덜도 말고 딱 2주동안 해서 700점 넘었다는게 너무 좋았다 (더 공부했으면 억울할 뻔)

그렇게 진짜로 졸업했다!! 우리학교 기준 평점 4.3 만점 3.85 학점으로 졸업을 할 수 있었다. 학점에 대한 비결은,, 나는 전공을 좋아해서 전공 학점은 거의 A대 였다. 그래서 전공을 늘리고 교양을 줄였는데, 교양은 대부분 패논패로 처리해서 최대한 전공학점이 반영이 되게 학점관리를 했었다. 그래서 전공학점을 기준으로하면 4.3 만점 4.05 학점이다. 나름 학교 생활을 성실하게 했다는 지표이기 때문에 이력서에도 열심히 넣었었다. (아.. 이력서 업뎃도 해야하는데..)

4-2. 학관 리모델링 재건축기금 기부

작년 2월 학관 리모델린 재건축기금 기부를 받는다고해서 이제 막 월급 따박따박 받는 사회초년생이 된 기념으로 바로 300만원 기부를 했었다. 후원자 기념판 이름에 등재된다고 해서 매우 기대가 된다. 학교에 기부해서 내 이름이 학교 어딘가에 걸려있는게 나름의 버킷리스트였는데, 당시 기회가 있을 때 잡자! 생각해서 일단 신청은 CMS로 했는데.. 다음날에 대외협력처에 바로 전화해서 그냥 일시불로 입금했던 기억이 있다. 지금처럼 당당하게 나를위한 삶을 살 수 있게 된게 우리학교를 다닌게 영향이 분명히 있고 학교에 애정이 있기 때문에 꼭 해보고 싶었다.

5. 일상

5-1. 운전 면허

5월인가 운전 면허를 땄다. 그동안 돈이 없다는 핑계로 못했던 거였는데, 직장인이 되고 일상, 수익이 안정되니까 이제 한번 해볼까? 하고 도전하게 되었다. 필기 기능 도로주행 모두 실패없이 한번에 통과했다!!! 그런데 지금 사는 곳이 교통이 좋아서 딱히 자차가 필요가 없어서 그런데 정작 차는 없어서 장롱면허가 될 예정이다ㅋㅋ 캐스퍼가 막 출시됐을때 무리해서라도 차를 살까 하는 뽐뿌가 엄청 왔었는데, 그래도 정작 써먹을 데를 생각해보니 없는 것같아서 한 30살에 가까워 질 때쯤 사야지 생각했다.
아 면허를 따면서 웃겼던 기억은..ㅋㅋㅋ 나는 길치인데, 도로주행 길이 진짜 너무 안외워져서 그냥 길이나 내가 해야하는 행동을 종이에 적어서 다 외워갔다. 운전학원 유튜브에 도로주행 연습하는 영상이 있었는데, 그 영상으로 보면서 "신한은행 간판이 보이면 우측 깜박이" > "이후 사거리에서 우회전 (핸들은 빠르게 한바퀴)" 이런식으로... 친구들이 듣고 경악했다. 나중에 실제로 운전해야 할 때는 연수 제대로 받고 해야지..

5-2. 머리색 근황

그동안 살면서 탈색을 한번도 안했었다. 다들 염색한번 해보는 시기인 고3 수능 끝나고에는 “한국사람이면 그냥 검정머리가 제일 이쁘지” 이렇게 생각하고 머리를 해도 파마만 했었는데ㅋㅋㅋ 그냥 문뜩 올해 5월 지금 20대 초반일때 탈색 한번도 안하면 평생에 앞으로 할 일 없지 않을까? 하고 질렀다 그러고 지금까지 유지중이다. 탈색하고 헤어제품도 엄청 많이사고 발열현상이나 등등 미용사 쌤한테 들으면서 알게 된게 많은데, 확실히 머리색을 바꿀 때 기분전환이 잘 되었다. 내 머리가 발열현상이 잘일어나서 탈색을 3번했는데도 백금발 탈색은 어려워서ㅠ 애쉬그레이 같은 색은 제대로 색이 안나와서 주로 붉은 계열로 염색을 한다. 근데 이게 탈색을 하다보니 머리색이 옅어지는 것 같기도..? 앞으로 한 일년정도 더 유지해볼까 고민중이다.

인스타에 #머리색근황 이렇게 하고 올려서 내사진만 뜬다.

5-3. 운동

재택근무를 하다가 어느순간 너무 오래 앉아있었는지 허리가 아파서 이제 슬슬 운동을 해야하나? 생각이 들었다. 재택근무를 위주로하니까 처음에는 의자를 시디즈로 바꾸는걸로 시작했는데, 그거와는 별개로 자취도하다보니 식습관이나 건강이 급 걱정이되었다. 작년 여름부터 계속 은님이 “조져지기 전에 운동하세요” 했었는데, 이러다가 조져지고 나서 운동하려나 싶어서 최대한 빨리 시작하자 하고 PT를 시작했다. PT를 하면서 아쉬웠던게 선생님이 많이 바뀌었던건데ㅠ 지금 하고있는 피티까지 다하면 40회정도되는데 선생님 변경만 3번이 있었다. 처음에 만났던 선생님이 잘맞아서 운동에도 재미붙이고 개인운동도 나가고하면서 20회를 결제했는데, 그 선생님이 퇴사를 하게되어서 7회는 중간에 다른 선생님이랑 하면서 또 적응하고, 새로운 헬스장으로 옮기면서 그 헬스장에서 다른 선생님이랑 지금 20회를 진행하고있다.
처음 받았던 쌤이랑 했을 때 매일매일 피티 받은 내용을 안잊으려고 쌤이 했던 조언들을 빼곡히 적어놨었다. 지금은 strong 어플을 사용하고있다.

확실히 지금 근력이 처음보다는 나아지고 관심이 많아지고있다는걸 느끼고있긴한데, 처음받았던 선생님이랑 쭉했으면 좋았겠다 싶어서 너무 아쉽다. 처음에 맨몸스쿼트만 하던내가 지금 랙에서 40KG 바벨들고 스쿼트 하고있다는거 보시면 와 처음에는 이랬는데, 지금은 이렇네요 이러면서 같이 뿌듯해하고 좋아해주셨을텐데 싶어서 말이다ㅠ. 그래도 처음 만났던 그 선생님 덕분에 뭔가 목표를 하나씩 깨는 느낌이 좋았었고, 주변에 재연언니나 은님 등등 운동에 관심많은 사람들이랑 얘기하면서 더 알아보다보니까 운동하는게 생각보다 재밌다. 원래는 슬라임, 달팽이 소리하면서 운동 귀찮아아 이러고 다녔는데 반년만에 생각이 바뀌다니 신기하다.
운동은 주에 3~4회정도 가고있다. 피티 받으면서 좀 더 스트레칭에 신경쓰려고 폼롤러 마사지도 많이 해주고있다. 개발자다보니 확실히 목 어깨 겨드랑이쪽 근육이 많이 뭉쳐서 업무 중간중간 마사지볼이나 폼롤러로 근육이 뭉치지 않게하려고 조금씩 해주고있다. 이렇게 해주면 좋은게 업무할 때도 리프레쉬 될 때도 있고, 상체운동 할 때 근육이 안뭉쳐있는 상태로 하면 덜 아프긴하더라. 여튼 피티를 시작으로 여러 운동에 관심이 갔고, 실제로 다른 운동들에도 좀 더 근력 더 키우고 해보고싶다라는 마음도 많이든다. 작년과 비교하면 내 인생에서 운동이 정말 큰 변화였던 것 같다.


이렇게 회고 끝!! GDSC 리드했던 멤버들이랑 매년 회고 릴레이를 하고있는데, 귀찮아 하면서도 막상 쓰면 엄청 길게 일년동안의 내 생각들을 주저리주저리 적고있는 것 같다. 1년치 일기장이라서 당연히 그럴 수 있지.

마무리는 대충 24살의 내사진

다들 회고록에 내년 목표를 적는데, 적당히 살자. 적당히 현생 챙기면서 살다보면 해피한 미래가 있겠지 👀 장기 계획세우면서 사는 타입이 아니라😳 2022년에도 활기차게 에너지 넘치는 쟈미로 살자 2021년 고생 많았다 안녕🙌🙌

Before I start this retrospective, “once again this year,” there were no issues with the KakaoTalk messaging server at year-end!!! Since I was the one directly handling the year-end response this time, I wanted to document the year-end experience too, which is why I ended up delaying this retrospective.

Working from home — so comfy~

I joined the company in September 2020 and started doing real hands-on work from December, and now it's 2022!! I can't believe I'm in my 3rd year?!! It's so surprising lol. I've really only been doing actual work for about 1 year and 1 month, yet I'm a 3rd-year developer.. That's amazing all over again.
So 2021 was a time for me to adapt as a working developer after joining the company, and since development became my profession, it was also a time to try things outside of coding to find a balance between work and life. With that, let's dive into the retrospective!

This retrospective feels like writing a year's worth of diary entries, so the readability isn't great.

 

1. Work

1-1. Treating work like homework?

I wrote daily work logs and tackled company tasks one by one, and comparing the first and second halves of the year, I can definitely feel a big difference.
In the first half, just deploying the dev version of the regular chat was thrilling and nerve-wracking, but by the second half, I was scaling up servers and deploying that same regular chat to the real/production version, tweaking ansible configs here and there for deployment settings, and eventually even handling the year-end response. Is this what it means to gain experience..?😳

My densely written work logs (contents are private)

There was definitely a noticeable difference in how I approached work between the first and second halves. In the first half, I kept wondering, "Am I really good enough to take on such a big task? I don't know much — should I really be taking on messaging work?" I hesitated and only worked on things that senior developers assigned to me. But in the second half, compared to the first, I started proactively seeking out work. I picked up a few issues that had been sitting around in our team, got closer with the client-side developers and actively communicated with them to provide messaging logic guidelines, and when I found something in the code that I wanted to fix, I'd create an issue and work on it myself. Early in the first half, a senior on my team told me, "Jyami, you do your work like it's homework." When I first heard this, I thought nothing of it — like, "Well, I've always done my homework well and with high quality. Does that mean I'm very meticulous?" But it kept bugging me, so I asked Eun about it and realized it meant I needed to be more proactive with my work.

1-2. What makes a good developer?

Still, I feel like I'm only picking up small, minor issues for now. There was a senior developer who volunteered to lead the C-to-Java porting effort, and I wonder if someday I'll be the one raising big issues like that too. Seeing things like this, I really feel there are so many people to learn from in the messaging team. But because of that, my thoughts on "what kind of developer should I be" have shifted since before and after joining the company. Before, I naturally thought since I love Java and Spring, I should dig deep into them and apply clean code, DDD, and all that stuff, and that once I started working, if I dug into Spring's internals, I'd become a good developer. But after joining, I've been realizing that knowing how to design better server architectures and properly use various components is also extremely important.

I think it's because when outages happen or when planning for new components, what matters more than how you write the code is the practical perspective — how to improve performance, how to handle edge cases, and so on. So before, when it came to using Redis, I used to think about which methods to use with spring-data-redis to write good code and good test code. But now, working in the messaging team, I've realized you need to think about Redis itself first. I've learned from experience that understanding Redis's data structures, failover policies, eviction policies, backup strategies, and so on is more important. So my thinking is shifting toward the idea that rather than being good at a particular language, a developer who can solve problems well when issues arise is probably the best to work with. I shared these thoughts with my team lead several times, and they advised me that everyone walks a different path and there's no single right answer for what makes a good developer.

1-3. Intern mentor / New hire code reviewer

Starting in the second half, interns and new hires began joining under me. I still feel like a newbie myself.. but I mentored an intern and am now responsible for code reviews of new hires who came in through the regular recruitment process. Honestly, I was a bit worried at first since some of them were older than me, but it turned out fine.✌️ After all, the new hire assignments were within the scope of what I knew, and anything I didn't know, I could just study up on.
Although I don't have a ton of experience myself, while mentoring the intern, I figured out what kind of person the company wants to hire as a new developer and guided them accordingly.

더보기
Many new developers, when working on their assignments, really want to perform like experienced developers — they want to show they can handle everything on their own without asking. So they dig deep into every requirement spec the mentor gives them, and as a result, they quantitatively meet the requirements. But in doing so, what's easy to miss is the "why" — "Why did you decide to develop it this way? Why did you think this should be the output?" Let's say there's a requirement for server performance testing. The new hire looks into it, gets a recommendation from their mentor to use a tool like ngrinder, confirms the server TPS looks good, and submits a results report. But then the final interviewer asks things like: "Why did you choose these comparison groups for the performance test?", "If N users access the server, this TPS might not be enough — do you have other metrics? / How could you improve it?" and so on. They value the logical reasoning behind your decisions — why you wrote the code this way, why you chose this tool, why the server is structured this way.

But I think it's honestly not easy for a mentor to anticipate all these questions and prep the mentee for each one. So ideally, the mentee should research various references on their own, compare the pros and cons of this tool versus others, ask the mentor for advice on how things are done in the team they're currently working in, and even if they don't implement it in the intern assignment, come prepared knowing about expansion plans and current shortcomings. The company seems to want to see that kind of passion and growth potential. Writing this out, I realize it's really not easy…😳


Working closely alongside the intern, I learned a lot, and I did my best to help them pass — advising them on everything from code to decision-making. But looking back, I think my enthusiasm might have been even greater than the intern's.. haha. That's when I most vividly felt that collaboration and managing people isn't easy, and I think I was going through some growing pains as a developer during that period. I started thinking about how, as I'll eventually become a senior to many people, what's the best way to give advice that's actually helpful. So these days, while doing code reviews for the new hires, I first share books and articles that are good to read at the code level and drop them in the PR🙌 (I tend to give pretty tough reviews.. so hang in there, my reviewees..heh)

But then again, writing all this out, it is tough ~_~ I'm originally the type who lives without plans (a hardcore ESTP), so I figure if I just do my best in the present, I'll probably turn out to be a pretty decent person in the future too.

2. Activities outside of development

This year, I cut back on community activities. Since I was the GDSC EWHA lead, one of the questions I got during my Kakao interview was, "Do you enjoy participating in developer communities?" And I stayed true to my answer: "During my 3-2 semester as lead, I felt proud of contributing to many people and building new experience. But that semester, my growth as a developer stalled. Between inviting speakers and preparing events, I didn't have enough time to study development, and my skills were about the same as in my 3-1 semester, which was frustrating. So if I join the company, I plan to step back from developer communities early on to focus on studying and adapting to work." I followed through on that. I've always found it easier to commit to NOT doing something rather than committing to doing something😝

But looking back, I actually ended up doing this and that..? Maybe I just can't help being an attention-seeker.

2-1. YouTube

My goal was to post on YouTube once a month. I wanted to focus my blog on technical content, but that didn't go well. Oh well, what can you do — at least I managed this much while living my life!! Hmm, as of January 16, 2022, my YouTube channel has 2,813 subscribers. It's amazing that people enjoy it even though I only upload casual VLOG videos. Meanwhile, I personally only watch YouTube for killing time and relaxation — variety shows (The Genius, Crime Scene, Workman, Civilization Express), gaming channels (Paka, Ssal Tube, Queen Lux) — so I'm a YouTuber who doesn't watch vlogs..! (proudly)🙄
People at work ask me about YouTube revenue, and the answer is none. To earn YouTube revenue, you need over 1,000 subscribers and 4,000+ hours of watch time, but since I don't upload many videos, I don't have enough watch time for ad revenue. Oh, but in the first half of the year, I got sponsorship and ad offers from Likelion and Mars Sleep, so I filmed some sponsored content. But making videos with sponsorships felt like work, which took the fun out of uploading YouTube videos, so I've been declining all sponsorships since then. Unless it's a dev-related product, I'll probably keep turning down cosmetics and other miscellaneous product requests lol (though I do think electronics or pouches could actually be useful🤔)

When I look at YouTube Studio analytics, it shows strategy insights, and surprisingly, "Code With Me" videos get a lot of views. So I kind of want to film them with nice interior decor and pretty lighting like other Code With Me channels..! But ugh, interior decorating is really hard for me;;; My decorating attempts always lack that aesthetic vibe. I'll do more research😵‍💫 As for the viewer demographic, since it's mostly developers watching, the age range is 18–34 and the gender ratio is 2:1 (male:female). It seems like a lot of people find my channel through Pangyo Walker (ah, I genuinely love this YouTuber — their videos are so fun), Jocoding, and Nomad Coders.

2-2. Blog

As for the blog, I actually spent more months reading books this year, and while I did take notes, lately I've been thinking "instead of re-reading my notes, I'd rather just re-read the book," so I've only saved them on Git and haven't organized them into blog posts. As a result, the number of posts on my blog dropped significantly. So my most popular blog posts are mostly retrospectives and job-landing stories,, hmm — a developer blog where retrospectives are more popular than technical posts 🙄 is this right? While working with my team, there are several topics I want to write about like ansible and other things, but ah, writing blog posts and editing YouTube videos on weekends is really not easy when weekends are so precious for office workers…😘 Let's try to make some time in 2022.

Oh wait, now that I think about it, I didn't write on my own blog — I wrote on the company blog lol. It feels pretty great to have my article on the Kakao Tech Blog.. It was something I'd always wanted to do, and a related team actually reached out to me first, haha. The posts are below.

https://tech.kakao.com/2021/05/24/jyami/

 

뉴크루의 카카오 백엔드 개발자 이야기

안녕하세요, 톡플랫폼개발팀 톡메시징파트의 신입 개발자 쟈미(jyami)입니다. 저는 자바, 코틀린을 좋아하는 백엔드 개발자이고, 카카오에 들어온 지는 약 반 년이 되어갑니다. 제가 카카오에 입

tech.kakao.com


https://tech.kakao.com/2021/10/25/grace-hopper-celebration/

 

Grace Hopper Celebration 참가기

카카오에서는 크루의 주도적인 성장과 역량 향상을 위해 글로벌한 인사이트 확대를 경험할 수 있도록 해외 컨퍼런스 참관 참여를 지원하고 있습니다. 안녕하세요, 톡플랫폼개발1팀의 jyami입니

tech.kakao.com


The "New Crew's Kakao Backend Developer Story" post above got shared a ton on Facebook at the time. Career/new developer topics are definitely super popular. If I write on the company blog again next time, I really want it to be about applying a specific technology. I'd been feeling like I've only been producing career-related content, which made me wonder, "Have I actually grown technically?"🙌 (But lately I haven't been studying… hmm…)

2-3. External interviews

In the first half, I got more interview requests from various places than I expected. They were all pretty similar — career-related stories about landing a job. Requests came from my university, college magazines, tech newsletters, and more, all featuring my story.

http://inews.ewha.ac.kr/news/articleView.html?idxno=32531

 

선배들의 따끈따끈 ‘취뽀’ 성공담 – 카카오(Kakao) 개발자 편 - 이대학보

코로나19의 여파로 취업 시장이 얼어붙었다. 청년층의 실업자 수는 2017년 이후 최고치를 기록했다. 이런 취업난에도 최근 다양한 분야에 취뽀(취업뽀개기)한 이화인들이 있다. 본지는 이들의 이

inews.ewha.ac.kr

https://maily.so/1step/posts/4a440c

 

📑 IT 대기업 입사는 공채만이 답?

프로합격러가 들려주는 주니어 상시채용 취준 꿀팁

maily.so

http://www.campl.co.kr/contents/content_read.asp?idx=10483

 

“하고 싶은 건 다 해보고, 끊임없이 배우는 것이 중요해요” 카카오톡 김민정

캠퍼스플러스

campl.co.kr

 

3. Study Groups

When I was a newbie, I felt like I had to study everything under the sun, so I pushed myself pretty hard in both the first and second halves of the year and ended up doing way more study groups than I expected. But looking back now, I kind of regret spreading myself so thin instead of just focusing deeply on a few things I was truly interested in. I've done all sorts of study groups by now, but it started feeling like I was wasting that time, so I quit everything and I'm currently taking a break. I want to study when I actually feel like it, and if I start a study group again, I want to focus on just one when I actually have the bandwidth for it.

3-1. Next Step Effective Kotlin (Feb – Mar)

I had been reading Kotlin in Action on my own but only got through about chapter 5, and since I never really had a chance to use it in practice, I only knew the parts of Kotlin that were compatible with Java syntax — I didn't feel like I truly understood Kotlin's own idioms and was using them properly. So I thought maybe if I tried writing Kotlin the way I did at Java-Bom and the Woowa Precourse, I'd get more comfortable with it, and signed up for the Next Step course. It was great to learn about Kotlin syntax and patterns I didn't know through code reviews, but doing the missions after work was really tough... So I only completed about 2–3 out of 4 missions and couldn't finish them all. Still, every piece of review feedback was genuinely helpful and I was grateful. I need to study it again separately… now that I'm starting Kotlin-related work, I'll have to dig deeper.
https://edu.nextstep.camp/c/Z9QeJlCi/

 

이펙티브 코틀린(Effective Kotlin) with TDD, Refactoring, Clean Code

edu.nextstep.camp


3-2. Java-Bom

This is a study group I've been part of since my junior year of college. Honestly, this group is a big reason I was able to study Java more intensively, and I think it helped a lot when I was job hunting. Every single member ended up landing a job — Baemin2 / Naver / Kakao / Marit — and despite that, we still kept going this year, steadily reading books little by little, asking questions, answering them, and looking things up together. You can see the list of books we covered this year at the link below, and at some point the repo crossed 200 stars!! Everyone puts a lot of effort into organizing and maintaining the repository, so it felt really proud and amazing to know that so many people were referencing it.
https://github.com/Java-Bom/ReadingRecord

 

GitHub - Java-Bom/ReadingRecord: 📚 책 읽고 정리하기 📚

📚 책 읽고 정리하기 📚. Contribute to Java-Bom/ReadingRecord development by creating an account on GitHub.

github.com

At Java-Bom, we covered a total of 3 books, and here's how I felt about each one. (The names in parentheses are the issue labels we use to manage them.)

  • Toby's Spring (Spring of Toby): This is definitely a must-read for any developer working with Spring. Some parts I skimmed through, others I read carefully, but if I ever revisit this book, I think I should read it alongside the actual Spring source code next time. The thing is, I read it at such a slow pace this time that I'd sometimes forget what I'd read earlier and just move on. Going forward, I want to read even thick books at a faster pace. (Easier said than done, though..)
  • Kubernetes Introduction (Kubernetes introduction): Hmm, personally I didn't find it very fun. The part about Kubernetes architecture that I wanted to dig deeper into — which I studied on my own by cross-referencing Kubernetes in Action and the official docs — was interesting, but the rest was basically typing out YAML files and being introduced to various features. Since I've already used Kubernetes before, the parts introducing each object's functionality via YAML were stuff I already knew, which is probably why. So I enjoyed Kubernetes in Action more than this introductory book. But if you've never learned Kubernetes and want a hands-on-lab style experience to follow along with, this book isn't bad either.
  • The Definitive Guide to Spring Batch (Definitive Guide to Spring Batch): I started this book hoping it would help me write better Spring Batch jobs, but the main takeaway ended up being just getting a better look at the batch repository structure. When I wasn't the one leading the session, I'd just kind of read through and move on. (Around this time I was starting to get tired of reading books.) The book introduces batch interfaces and briefly explains various implementations, but if you just note "oh, this exists" and don't actually use it yourself, it doesn't really stick — that's how I felt about it. I'm definitely the type who prefers getting a feel for things by actually coding rather than just reading. But if there's a specific implementation you want to use, I think you could get a quick overview from this book and then consult the official Spring Batch docs for a fast understanding.

The Java-Bom study group is currently on pause. Everyone's busy with work and motivation is low, so we decided to take a break.

3-3. System Design Study Group (Aug – Nov)

  • System Design Interview – An Insider's Guide

This study group was organized by Jeongdong, whom I got to know through GDSC. I ended up studying with completely new developers I had no prior connection with, and we read the book as a group from Baemin / Toss / AWS / Kakao. The great thing about reading this book with people from different companies was hearing real-world stories about how system designs are actually used in each company's domain. The book covers design topics at a fairly high level, so the content could feel lightweight on its own, but it was surprisingly fun as each member filled in the gaps with their own experiences and knowledge. Through this study group, I started thinking that beyond the application code I mentioned above, I should also become a developer who pays more attention to system design.

Jeongdong shared some great article links and YouTube videos for studying design, and my goal is to go through all of them this year and upload them to my blog one by one. Reading this book alone left some gaps. Also, while reading this book, I asked a senior developer on our team how you can estimate things like TPS per server in interviews, and they said it's a matter of experience. So I got the insight that it's important to remember or document the number of servers our team uses, the corresponding TPS, and the architecture. In fact, during retrospectives for outages or big events like year-end traffic spikes, we go over various numerical metrics, and it's really great every time we do it.


3-4. Book Study Group (Jan – Nov)

This is a study group run by Wonbeom, whom I also met through GDSC, along with his college friends. I don't remember how I ended up joining, but unlike Java-Bom, this group reads and reviews books on an incredibly fast cycle. Once we pick a book, we meet twice a week, and each time we need to have read one chapter (about 60–70 pages). I was used to reading books at a leisurely pace, and I'm the type who struggles to read even one chapter a day (I genuinely hate reading books unless they're novels). But I just told myself "let's push through and at least read one chapter a day" and that changed me. The upside was that I ended up reading way more books than expected, but I also often had to rush past parts I wanted to focus on because of time pressure.

  • Microservices Patterns: This was the very first book we read so I don't remember it too well 😢 But I do recall looking at various patterns and diagrams and trying my best to understand them by mapping them to examples I already knew. Since our team was already using some of these patterns and not others, this approach to understanding was possible. The hardest part was definitely the distributed transactions section.
  • DDD Start: Since some study members didn't know DDD, I thought it would've been better to read this book before the MSA book above. What was great was learning that the things I'd been calling "object-oriented" were actually named with specific terms like Aggregate, Domain, Domain Model, etc. I did read this book, but if you asked me "are you doing DDD?" I'd probably say I don't think I'm truly understanding and applying it properly — because I'm not consciously aware of those terms when I implement things. The book itself is explained very simply, so it's great for someone who's literally just starting with DDD.
  • Refactoring (I posted the 2nd edition cover but actually read the 1st edition in Java): Since I was assigned a refactoring task at work, I had high hopes that this book would be super helpful. Honestly, I was a little disappointed in some ways. Some of the patterns and strategies introduced here can be done with IntelliJ IDE features — I guess it can't be helped since it's an older book. But I do agree it deserves to be called a must-read for developers.
  • Kubernetes in Action: I was already writing Kubernetes YAML, but it felt great to fill in the deeper, more specific knowledge gaps one by one. However, as I got into the later chapters covering objects I'd never actually used, it became hard to understand or think of applications. That said, I really dug into the Kubernetes architecture components like etcd, API server, kubelet, and others through this book.
  • Designing Data-Intensive Applications: Oh man, this one was really hard. I was reading this alongside the System Design Interview book, and that's around when I really felt the need to study distributed system design more seriously. But seriously, it's really hard. Not everything in the book was useful to me, but roughly the first two-thirds was excellent. The latter part covered data analysis topics like Hadoop and Spark, which just didn't interest me.

I wrapped up this study group too. All my study groups this year were just book study groups, and I ended up only reading books during study time without ever actually implementing code or digging deeper, which gave me a bit of an existential crisis. So now I've quit everything! Time to refresh and get back to studying what I actually want.

3-5. Internal Company Study Groups

  • Redis study group: This was an internal study group where most of our team members participated. Here, I also gave an internal presentation on how to use redis-cluster with k8s helm. At the time, I analyzed the bitnami repo and presented what commands were being executed internally to enable certain configurations. However, I'm not currently involved in the redis-cluster migration work our team is doing. I attended the progress presentations from the people working on the migration, and I was really impressed — it can't be easy to study so much to apply what you've learned in practice, keeping everything from config verification to monitoring in mind.
  • Advanced Programming in the UNIX Environment: I'm doing this with a junior developer who joined 9 months before I did, just the two of us. Since our team has some code written in C, I started reading this book hoping it might lower the barrier to entry a bit. Umm... it's hard. There are explanations of UNIX functions and conventions that are sometimes hard to relate to, and sometimes I think I should have paid more attention in my operating systems and computer architecture classes. But since I started this study group with a coworker, we'll probably muddle through to the end somehow.


3-6. Books I Read on My Own

  • Netty in Action: I had previously read this when I needed Netty for the project I did during my conversion from probationary to full-time employee. But since the intern's project also used Netty, I read it again to give proper code reviews and guidance. It's a quick read once you've been through it, but trying to actually apply and build something with it was not easy.
  • Kotlin in Action: I started reading it but never finished 😢 I felt like I wouldn't truly internalize it unless I followed along and ran each example from the book, so I stopped just reading with my eyes. But then it got pushed to the back burner by other book study groups.
  • Redis Operations & Management: Simple and easy to read, packed with nothing but golden tips. Even better that it's under 100 pages. My brief summary is on my blog: https://jyami.tistory.com/141
  • The Programmer's Brain: Currently reading this. I got tired of only reading dev books, so I picked this up as kind of a developer humanities (?) read. As I'm reading it though, I'm starting to think "wait, is my brain basically a computer?" It describes human memory using analogies of RAM, disk, and CPU.

 

4. Ewha Womans University

4-1. TOEIC and Graduation

I graduated!!! Finally!! I almost couldn't graduate because of the TOEIC score requirement, but I passed that too!!! Ideally, I should have taken the TOEIC at the end of 2020 and graduated in February 2021 right on time, but at the end of 2020, I was preparing for my Kakao full-time conversion interview and didn't keep track of the graduation requirements. So I deferred and aimed for August 2021 graduation... but I forgot about that too, and was just living life until around June when it hit me. I calculated the TOEIC score submission deadline and had exactly 2 weeks to study.
The last time I took the TOEIC at the end of 2020 with zero preparation, I scored 565. But now I needed to hit 700 within 2 weeks..!! I wish I'd studied earlier, but I kept putting it off and ended up needing to raise my score by 135 points in just 2 weeks. So at the time, I was going to work at 9 AM, getting off at 6 or 7 PM, eating dinner, studying TOEIC until 10–11 PM, then playing a game or two of LoL to unwind. The result? 710 points. My goal wasn't to actually learn English — I was purely focused on graduating, so I only needed 700. Getting exactly past 700 in just 2 weeks felt amazing (if I'd studied any more, I would've felt robbed).

And just like that, I actually graduated!! By my school's standards, I graduated with a 3.85 GPA out of 4.3. My secret to the GPA? I loved my major, so my major courses were almost all A-range. I loaded up on major courses and cut down on liberal arts, and handled most liberal arts as pass/fail so my GPA would mostly reflect my major courses. So my major-only GPA is 4.05 out of 4.3. Since it's an indicator that I was pretty diligent during school, I made sure to put it on my resume. (Ah... I need to update my resume too...)

4-2. Student Union Building Renovation Fund Donation

Last February, the school was accepting donations for the student union building renovation fund, so as a fresh working adult who had just started receiving a steady paycheck, I went ahead and donated 3 million won right away. They said my name would be engraved on the donor memorial plaque, which I'm super excited about. Having my name displayed somewhere on campus was one of my bucket list items, and I thought "seize the opportunity while it's here!" I initially signed up via automatic monthly payments (CMS), but the next day I called the Office of External Affairs and just wired the whole amount in a lump sum. Being able to live confidently and on my own terms is definitely partly thanks to attending this school, and I have real affection for it, so I really wanted to do this.

5. Daily Life

5-1. Driver's License

I got my driver's license around May. I'd been putting it off using the excuse that I didn't have money, but once I became a working adult and my daily life and income stabilized, I thought "why not give it a shot?" and went for it. I passed the written test, skills test, and road test all on the first try!!! But since I live in an area with great public transportation and don't really need a car, I don't actually own one, so it's destined to be a dusty license lol. When the Casper first launched, I was seriously tempted to splurge on a car, but when I thought about where I'd actually use it, I couldn't think of anywhere, so I figured I'd buy one when I'm closer to 30.
Oh, a funny memory from getting my license... I'm terrible with directions, and I absolutely could not memorize the road test route, so I just wrote down the roads and every action I needed to take on paper and memorized it all. There was a YouTube video from the driving school showing the road test practice route, and I watched it while writing things like "When you see the Shinhan Bank sign, turn on right blinker" > "Then make a right turn at the intersection (turn the wheel one full rotation quickly)"... My friends were horrified when they heard. When I actually need to drive for real someday, I'll make sure to get proper driving lessons first..

5-2. Hair Color Update

I had never bleached my hair in my entire life. Back when everyone experiments with hair color — right after the college entrance exam in senior year — I was like "Koreans look best with black hair" and only ever got perms lol. Then out of the blue in May this year, I thought "I'm in my early 20s — if I don't bleach my hair now, I'll probably never do it for the rest of my life," and I just went for it. I've been keeping it up since then. After bleaching, I bought a ton of hair products and learned a lot from my hairstylist about things like heat reactions and whatnot. Changing my hair color was definitely a great mood booster. My hair tends to have strong heat reactions, so even after bleaching 3 times, platinum blonde is hard to achieve 😢 Colors like ash gray don't come out right, so I mainly go with reddish tones. But I feel like my hair color is getting lighter with each bleach...? I'm debating whether to keep it up for about another year.

I post on Instagram with #haircolorupdate so only my photos show up.

5-3. Working Out

While working from home, at some point my back started hurting from sitting too long, and I thought maybe it's time to start exercising. Since I was mostly working from home, I first started by switching to a Sidiz chair, but aside from that, being on my own with my own cooking made me suddenly worried about my diet and health. Starting from last summer, Eunnim kept telling me "exercise before your body gives out," and I started thinking I'd probably end up exercising only after my body breaks down, so I decided to start personal training as soon as possible. The frustrating part about PT was that my trainer kept changing 😢 Including my current PT sessions, I've done about 40 sessions total, but I've had 3 trainer changes. My first trainer was a great fit — I got into working out, started going to the gym on my own, and bought 20 more sessions. But that trainer quit, so I did 7 sessions with a different trainer while adjusting, then switched to a new gym where I'm now doing 20 sessions with yet another trainer.
When I was with my first trainer, I wrote down all the advice they gave me after every session so I wouldn't forget. Now I use the Strong app.

I can definitely feel that my strength has improved since the beginning and I'm getting more interested, but I really wish I could have continued with my first trainer. I went from doing bodyweight squats to now squatting with a 40KG barbell on the rack — if my first trainer could see that, they'd say "wow, you started like this and now look at you!" and be proud together with me 😢. Still, thanks to that first trainer, I got that great feeling of crushing goals one by one, and talking with people around me who are into fitness — like Jaeyeon and Eunnim — and learning more from them made working out surprisingly fun. I used to go around whining like a slug or a snail saying "ugh, exercise is so annoying," and it's wild that my mindset changed in just half a year.
I go to the gym about 3–4 times a week. With PT sessions, I'm also trying to focus more on stretching and do lots of foam roller massages. Being a developer, the muscles around my neck, shoulders, and armpits definitely get really tense, so I try to use a massage ball or foam roller between work to keep the muscles from knotting up. It's nice because it also refreshes me during work, and when I do upper body exercises without knotted-up muscles, it hurts less. Anyway, starting PT led me to get interested in various types of exercise, and I genuinely want to build more strength and try other workouts too. Compared to last year, exercise has been the biggest change in my life.


And that's the end of my retrospective!! The GDSC lead members and I do a yearly retrospective relay, and even though I always grumble about how bothersome it is, once I actually start writing, I end up going on and on about my thoughts from the past year. I guess that's natural since it's basically a year's worth of diary entries.

Wrapping up with some random photos of me at 24

Everyone writes their goals for next year in their retrospectives, but I'll just say: let's take it easy. If I just keep taking care of my daily life, there'll be a happy future ahead, right? I'm not the type to make long-term plans 😳 Let's live as an energetic, vibrant Jyami in 2022 too! 2021, it's been tough — goodbye 🙌🙌

댓글

Comments

Develop/Kotlin

Ktor 찍먹하기 - 간단 HTTP API 작성 | Trying Out Ktor - Writing a Simple HTTP API

ktor 공식문서 따라해 보는 중 : https://ktor.io/docs/gradle.html#create-entry-point Adding Ktor to an existing Gradle project | Ktor ktor.io나의 소스코드 : https://github.com/jyami-kim/ktor-sample 1. 서버 실행을 위한 기본 골격- ktor embeddedServer로 실행 : https://ktor.io/docs/gradle.html#create-embedded-server- ktor engineMain으로 실행 : https://ktor.io/docs/gradle.html#create-engine-maina. embeddedServer package com.exampleimpo..

Ktor 찍먹하기 - 간단 HTTP API 작성 | Trying Out Ktor - Writing a Simple HTTP API

728x90

ktor 공식문서 따라해 보는 중 : https://ktor.io/docs/gradle.html#create-entry-point

 

Adding Ktor to an existing Gradle project | Ktor

 

ktor.io

나의 소스코드 : https://github.com/jyami-kim/ktor-sample

 

1. 서버 실행을 위한 기본 골격

- ktor embeddedServer로 실행 : https://ktor.io/docs/gradle.html#create-embedded-server
- ktor engineMain으로 실행 : https://ktor.io/docs/gradle.html#create-engine-main

a. embeddedServer 

package com.example

import io.ktor.application.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*

fun main() {
    embeddedServer(Netty, port = 8080) {
        routing {
            get("/") {
                call.respondText("Hello, world!")
            }
        }
    }.start(wait = true)
}

기본 서버 실행을 위한 골격. routing에 해당하는 부분을 주로 아래와 같이 configureXXX 와 같은 형태의 플러그인 형식으로 빼서, 분리하는 패턴을 갖고있음. (코틀린의 확장함수 사용)

// com.jyami.Application

fun main() {
    embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
        configureRouting()
    }.start(wait = true)
}

// com.jyami.plugins.Routing

fun Application.configureRouting() {

    routing {
        get("/") {
            call.respondText("Hello World!")
        }
    }
}

 

위와같이 간단한 코드만 짜서 서버 RUN을 시키면? 
서버 실행 속도가 이렇게까지 빨라도 되는건가.. spring안쓰고 간단하게 서버 만들고 싶을 때 쓰면 좋을 것 같다.

 

b. engineMain (HOCON 포맷)

위 예제는 embeddedServer로 짜는 방법을 적었는데, 만약 EngineMain 방식으로 ktor 서버를 짜고 싶다면 조금 다른 방식이다. 

// main.kotlin.com.jyami

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

fun Application.module() {
    configureRouting()
}


// main.kotlin.com.jyami.plugins

fun Application.configureRouting() {
    routing {
        get("/") {
            call.respondText("Hello World!")
        }
    }
}


// resource

ktor {
    deployment {
        port = 8080
    }
    application {
        modules = [ com.jyami.ApplicationKt.module ]
    }
}

HOCON 포맷이라고한다. resources 폴더안에 application.conf 파일을 이용해서 실행시킬 포트나, entry point에대한 애플리케이션 설정을 작성한다. application.conf 관련 여러 설정들 : https://ktor.io/docs/configurations.html

 

2. gradle 설정 확인

gradle application 플러그인 : https://docs.gradle.org/current/userguide/application_plugin.html

 

The Application Plugin

This plugin also adds some convention properties to the project, which you can use to configure its behavior. These are deprecated and superseded by the extension described above. See the Project DSL documentation for information on them. Unlike the extens

docs.gradle.org

 

실행가능한 JVM 애플리케이션을 만들 수 있는 기능을 제공함. 
개발도중에 쉽게 애플리케이션을 시작할 수 있도록 해주며, TAR, AIP과 같이 애플리케이션 패키지도 지원해줌

application 플러그인 안에 java / distribution 플러그인이 모두 내장되어있다. 각각 main에 대한 소스 셋, 배포를 위한 패키징 기능을 가진 플러그인 인듯 하다.

application 플러그인을 사용할 때  configuration은 main class (i.e. entry point)를 지정하는 건 꼭 필요하다.

plugins {
    application
    kotlin("jvm") version "1.6.0"
}
application {
    mainClass.set("com.kakao.ApplicationKt")
}

실행시 intellij runner사용도 되고, ./gradlew run을 사용해도 된다.

 

3. 튜토리얼 1: HTTP API 만들기

build.gradle 분석

dependencies {
    implementation("io.ktor:ktor-server-core:$ktor_version")
    implementation("io.ktor:ktor-server-netty:$ktor_version")
    implementation("ch.qos.logback:logback-classic:$logback_version")
    implementation("io.ktor:ktor-serialization:$ktor_version")

    testImplementation("io.ktor:ktor-server-tests:$ktor_version")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
}
  • ktor-server-core : ktor가 제공하는 핵심 컴포넌트 제공
  • ktor-server-netty : 네티엔진을 사용하여 서버 기능을 사용할 수 있게한다. 외부 애플리케이션 컨테이너에 의존하지 않고도 가능.
  • logback-classic : SLF4J 구현
  • ktor-serialization : 코틀린 객체를 JSON과 같은 직렬화 형태로 변환해준다. 
  • ktor-server-test-host : http 스택을 사용하지 않고도 서버 테스트가 가능함. unit test로 사용가능함

 

ConentNegotiation

fun Application.module() {
    install(ContentNegotiation){
        json()
    }
}
  • 클라이언트와 서버간의 미디어타입을 중계해준다. : Accept, Content-Type Header
  • kotlinx.serialization 라이브러리 혹은 그외의 것 (Gson, Jackson)을 사용하여 컨텐츠를 특정 포맷으로 serializing/deserializing한다. (json으로 하겠지)

ContentNegotiation 플러그인을 사용하기 위하여 애플리케이션 초기화 코드에 install 함수를 사용하여 해당하는 ConetentNegotiation 플러그인을 넘겨준다.

ktor에서는 Content-Type으로 다양한 내장 함수를 제공하고 있는데 

  • kotlinx.serializtion : JSON, Protobuf, CBOR
  • Gson : JSON
  • Jackson : JSON

그이외에 커스텀한 Content-Type을 제공하고 싶다면 아래와 같이 ContentNegotiation의 register 메서드를 사용하여 등록하면 된다

install(ContentNegotiation) {
    register(ContentType.Application.Json, CustomJsonConverter())
    register(ContentType.Application.Xml, CustomXmlConverter())
}

 

request, response 파라미터 제어

1. path parameter

get("{id}") {
    val id = call.parameters["id"] ?: return@get call.respondText(
        "Missing or malformed id",
        status = HttpStatusCode.BadRequest
    )
    val customer = customerStorage.find {it.id == id} ?: return@get call.respondText (
        "No customer with id $id",
        status = HttpStatusCode.NotFound
    )
    call.respond(customer)
}

인덱스 접근자 함수 : call.parameters["myParamName"]
요청 Path를 기본적으로 string으로 가져오게 된다.

 

2. request body

post {
    val customer = call.receive<Customer>()
    customerStorage.add(customer)
    call.respondText ("Customer stored correctly", status = HttpStatusCode.Created)
}

call.recevie<T> : 제네릭 변수를 사용하여 호출한 request body를 자동으로 코틀린 객체로 역직렬화 한다. 

동시에 여러 요청이 접근될 때 문제는 이 실습의 범위를 벗어난다 : 동시에 요청/스레드에서 액세스 할 수 있는 데이터 구조 혹은 코드를 작성하면 될 것

 

3. response body

get {
    if (customerStorage.isNotEmpty()) {
        call.respond(customerStorage)
    } else {
        call.respondText("No customers found", status = HttpStatusCode.NotFound)
    }
}
  • call.respond() : kotlin 객체를 가져와 해당 객체가 지정된 형식으로 직렬화하여 http 응답을 반환한다.
  • call.respondText() : Text를 응답으로 보낼 때의 respond를 간단하게 구현해둔 함수이다. 문자열 응답을 반환한다.

 

Routing

Route 확장함수를 이용하여 경로를 정의하였다. 이렇게 할 경우 아무래도 라우팅만 역할을 빼서 코드를 관리할 수 있어서 좋아보인다. 
Application.module에 있는 라우팅 블록 내부에 각 경로를 직접 추가할 수 있긴하지만, 파일 경로들을 그룹화하여 관리하는 것이 유지보수에 더 좋다. 

Route 확장함수만을 사용하여 경로를 정의할 경우 실제 Ktor 서버에는 적용되지 않는데, 맨 처음 ktor 시작하기에 했던 것처럼, route를 Application에 등록해주는 과정이 필요하다. 즉, Route.module에 등록된 경로들을 Application.module에 등록하는 방식으로 관리하자.

Application의 확장함수를 사용함으로써 application에 등록할 path를 route dsl을 이용하여 등록할 수 있으며, 이 dsl에는 사실상 Route 모듈에 등록된 경로를 Application에 경로를 등록하도록 도와주는 것으로 보인다.

fun Route.customerRouting() {
    route("/customer") {
    	get{}
    }
}

fun Application.registerCustomerRoutes() {
    routing {
        customerRouting()
    }
}

fun main() {
    embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
        registerCustomerRoutes()
        install(ContentNegotiation){
            json()
        }
    }.start(wait = true)
}

 

4. Ktor 테스트

ktor-server-test-host : netty를 시작하지 않고도 endpoint 테스트가 가능하다.
테스트 request를 실행하기 위한 몇가지 헬퍼 메서드가 프레임워크에서 제공되며, 그중 하나가 TestApplication 이다.

HOCON방식 : 한번에 모든 configuration이 등록가능한 구조

internal class OrderRoutingKtTest {

    @Test
    fun testGetOrder() {
        withTestApplication({ module() }) {
            handleRequest(HttpMethod.Get, "/order/2020-04-06-01").apply {
                assertEquals(
                    """{"number":"2020-04-06-01","contents":[{"item":"Ham Sandwich","amount":2,"price":5.5},{"item":"Water","amount":1,"price":1.5},{"item":"Beer","amount":3,"price":2.3},{"item":"Cheesecake","amount":1,"price":3.75}]}""",
                    response.content
                )
                assertEquals(HttpStatusCode.OK, response.status())
            }
        }

    }
    
}

HOCON 포맷인 경우에 사용이 가능한 것으로 보인다. (embeddedServer 방식으로 짜는 경우에는 httpClient를 사용한 직접 호출 방식을 이용하는 것으로 보인다 : https://ktor.io/docs/testing.html#end-to-end)

핵심은 withTestApplication인데, 테스트로 실행하려는 애플리케이션을 주입한다. (Application.module() 형태로된 메서드면 무엇이든 주입이 가능한 것으로 보인다.)

HOCON 포맷의 경우에는, main에 넣기전 main밖에 있는 Application 확장함수에 정의되어있어, 해당 애플리케이션에 대한 모든 설정이 등록된채 테스트가 가능하고, 따라서 위와 같이 module() 을 넣고 테스트를 해도 무관하다.

 

embeddedServer 방식 : 필요모듈만 configuration에 등록하여 테스트할 수 있는 구조

fun Application.testModule(){
    registerOrderRoute()
    install(ContentNegotiation){
        json()
    }
}

@Test
fun testGetOrder() {
    withTestApplication({ testModule() }) {
        handleRequest(HttpMethod.Get, "/order/2020-04-06-01").apply {
            assertEquals(
                """{"number":"2020-04-06-01","contents":[{"item":"Ham Sandwich","amount":2,"price":5.5},{"item":"Water","amount":1,"price":1.5},{"item":"Beer","amount":3,"price":2.3},{"item":"Cheesecake","amount":1,"price":3.75}]}""",
                response.content
            )
            assertEquals(HttpStatusCode.OK, response.status())
        }
    }

}

다만 embeddedServer 방식으로 withTestApplication을 작성하는 경우에는, main() 함수안에 직접 선언이 되어있어 애플리케이션에 대한 모든 테스트는 불가능하고 위에서 내가 정의한 configureRouting(), registerCustomRouting(), registerOrderRouting()과 같이 Application 확장함수 단위로 테스트가 가능하다. 

다만 여기서 주의해야할 점은 configureRouting은 단일하게 넣어도 테스트가 가능하나, registerOrderRouting, registerCustomRouting의 경우에는 불가능하다 왜냐하면 이것들을 직접 넣고 돌리게되면, Application에 등록해둔 ContentNegotiation이 빠지게 될것이고, serialize과정에 문제가 생겨서 테스트가 실패하게 될 것이다. 따라서 테스트시 두개의 configuration을 함께 등록하여 테스트해야한다.

위와같이 테스트가 가능하게 되면서, ktor을 사용할 경우에는 모듈별로, 설정별로 테스트가 손쉽게 될 것으로 보인다.
사실, embeddedServer나 HOCON이나 하나의 Application 확장함수를 만들어서 테스트하는건 같아서 입맛대로 사용하도록하자.

 

Following along with the ktor official docs: https://ktor.io/docs/gradle.html#create-entry-point

 

Adding Ktor to an existing Gradle project | Ktor

 

ktor.io

My source code: https://github.com/jyami-kim/ktor-sample

 

1. Basic Skeleton for Running a Server

- Running with ktor embeddedServer: https://ktor.io/docs/gradle.html#create-embedded-server
- Running with ktor engineMain: https://ktor.io/docs/gradle.html#create-engine-main

a. embeddedServer 

package com.example

import io.ktor.application.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*

fun main() {
    embeddedServer(Netty, port = 8080) {
        routing {
            get("/") {
                call.respondText("Hello, world!")
            }
        }
    }.start(wait = true)
}

This is the basic skeleton for running a server. The routing part is typically extracted into a plugin-style pattern like configureXXX as shown below, separating concerns. (Using Kotlin's extension functions)

// com.jyami.Application

fun main() {
    embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
        configureRouting()
    }.start(wait = true)
}

// com.jyami.plugins.Routing

fun Application.configureRouting() {

    routing {
        get("/") {
            call.respondText("Hello World!")
        }
    }
}

 

If you write just this simple code and RUN the server? 
Is it even okay for the server startup to be this fast.. Seems like it would be great for quickly spinning up a server without using Spring.

 

b. engineMain (HOCON Format)

The example above showed how to write it using embeddedServer. If you want to build a ktor server using the EngineMain approach, it's a slightly different method. 

// main.kotlin.com.jyami

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

fun Application.module() {
    configureRouting()
}


// main.kotlin.com.jyami.plugins

fun Application.configureRouting() {
    routing {
        get("/") {
            call.respondText("Hello World!")
        }
    }
}


// resource

ktor {
    deployment {
        port = 8080
    }
    application {
        modules = [ com.jyami.ApplicationKt.module ]
    }
}

It's called the HOCON format. You write the application configuration — such as the port to run on and the entry point — in an application.conf file inside the resources folder. Various settings for application.conf: https://ktor.io/docs/configurations.html

 

2. Checking the Gradle Configuration

Gradle application plugin: https://docs.gradle.org/current/userguide/application_plugin.html

 

The Application Plugin

This plugin also adds some convention properties to the project, which you can use to configure its behavior. These are deprecated and superseded by the extension described above. See the Project DSL documentation for information on them. Unlike the extens

docs.gradle.org

 

It provides the ability to create executable JVM applications. 
It makes it easy to start the application during development and also supports application packaging like TAR and ZIP.

The application plugin has both the java and distribution plugins built in. Each seems to be a plugin that provides source sets for main and packaging functionality for distribution, respectively.

When using the application plugin, specifying the main class (i.e. entry point) in the configuration is mandatory.

plugins {
    application
    kotlin("jvm") version "1.6.0"
}
application {
    mainClass.set("com.kakao.ApplicationKt")
}

You can run it using the IntelliJ runner or by using ./gradlew run.

 

3. Tutorial 1: Building an HTTP API

Analyzing build.gradle

dependencies {
    implementation("io.ktor:ktor-server-core:$ktor_version")
    implementation("io.ktor:ktor-server-netty:$ktor_version")
    implementation("ch.qos.logback:logback-classic:$logback_version")
    implementation("io.ktor:ktor-serialization:$ktor_version")

    testImplementation("io.ktor:ktor-server-tests:$ktor_version")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
}
  • ktor-server-core: Provides the core components offered by ktor
  • ktor-server-netty: Enables server functionality using the Netty engine, without depending on an external application container.
  • logback-classic: SLF4J implementation
  • ktor-serialization: Converts Kotlin objects into serialized formats like JSON. 
  • ktor-server-test-host: Allows server testing without using the HTTP stack. Can be used as unit tests.

 

ContentNegotiation

fun Application.module() {
    install(ContentNegotiation){
        json()
    }
}
  • Mediates media types between the client and server: Accept, Content-Type Header
  • Uses the kotlinx.serialization library or others (Gson, Jackson) for serializing/deserializing content into a specific format (JSON in our case).

To use the ContentNegotiation plugin, you pass the ContentNegotiation plugin using the install function in the application initialization code.

Ktor provides various built-in functions for Content-Type:

  • kotlinx.serialization: JSON, Protobuf, CBOR
  • Gson: JSON
  • Jackson: JSON

If you want to provide a custom Content-Type beyond those, you can register it using ContentNegotiation's register method like this:

install(ContentNegotiation) {
    register(ContentType.Application.Json, CustomJsonConverter())
    register(ContentType.Application.Xml, CustomXmlConverter())
}

 

Controlling Request and Response Parameters

1. path parameter

get("{id}") {
    val id = call.parameters["id"] ?: return@get call.respondText(
        "Missing or malformed id",
        status = HttpStatusCode.BadRequest
    )
    val customer = customerStorage.find {it.id == id} ?: return@get call.respondText (
        "No customer with id $id",
        status = HttpStatusCode.NotFound
    )
    call.respond(customer)
}

Index accessor function: call.parameters["myParamName"]
It retrieves the request path as a string by default.

 

2. request body

post {
    val customer = call.receive<Customer>()
    customerStorage.add(customer)
    call.respondText ("Customer stored correctly", status = HttpStatusCode.Created)
}

call.receive<T>: Uses a generic type parameter to automatically deserialize the request body into a Kotlin object. 

Handling concurrent request access is beyond the scope of this tutorial — you'd just need to write data structures or code that can handle simultaneous requests/threads.

 

3. response body

get {
    if (customerStorage.isNotEmpty()) {
        call.respond(customerStorage)
    } else {
        call.respondText("No customers found", status = HttpStatusCode.NotFound)
    }
}
  • call.respond(): Takes a Kotlin object, serializes it into the specified format, and returns it as an HTTP response.
  • call.respondText(): A convenience function for sending text as a response. Returns a string response.

 

Routing

Routes are defined using Route extension functions. This approach is nice because it lets you separate and manage just the routing logic on its own. 
While you could add each route directly inside the routing block in Application.module, grouping file paths together is better for maintainability. 

If you only define routes using Route extension functions, they won't actually be applied to the Ktor server. As we did in the initial ktor setup, you need to register the routes with the Application. In other words, manage it by registering the routes defined in Route.module into Application.module.

By using Application extension functions, you can register paths to the application using the route DSL. This DSL essentially helps register the routes defined in the Route module into the Application.

fun Route.customerRouting() {
    route("/customer") {
    	get{}
    }
}

fun Application.registerCustomerRoutes() {
    routing {
        customerRouting()
    }
}

fun main() {
    embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
        registerCustomerRoutes()
        install(ContentNegotiation){
            json()
        }
    }.start(wait = true)
}

 

4. Testing in Ktor

ktor-server-test-host: Allows endpoint testing without starting Netty.
The framework provides several helper methods for executing test requests, and one of them is TestApplication.

HOCON approach: A structure where all configurations can be registered at once

internal class OrderRoutingKtTest {

    @Test
    fun testGetOrder() {
        withTestApplication({ module() }) {
            handleRequest(HttpMethod.Get, "/order/2020-04-06-01").apply {
                assertEquals(
                    """{"number":"2020-04-06-01","contents":[{"item":"Ham Sandwich","amount":2,"price":5.5},{"item":"Water","amount":1,"price":1.5},{"item":"Beer","amount":3,"price":2.3},{"item":"Cheesecake","amount":1,"price":3.75}]}""",
                    response.content
                )
                assertEquals(HttpStatusCode.OK, response.status())
            }
        }

    }
    
}

This appears to be usable with the HOCON format. (For the embeddedServer approach, it seems you'd use a direct call method with httpClient instead: https://ktor.io/docs/testing.html#end-to-end)

The key part is withTestApplication, which injects the application you want to run for testing. (It looks like any method in the form of Application.module() can be injected.)

With the HOCON format, since the Application extension function is defined outside of main before being put into main, all configurations for that application are registered and can be tested. So testing with module() as shown above works just fine.

 

embeddedServer approach: A structure where you can register only the needed modules in the configuration for testing

fun Application.testModule(){
    registerOrderRoute()
    install(ContentNegotiation){
        json()
    }
}

@Test
fun testGetOrder() {
    withTestApplication({ testModule() }) {
        handleRequest(HttpMethod.Get, "/order/2020-04-06-01").apply {
            assertEquals(
                """{"number":"2020-04-06-01","contents":[{"item":"Ham Sandwich","amount":2,"price":5.5},{"item":"Water","amount":1,"price":1.5},{"item":"Beer","amount":3,"price":2.3},{"item":"Cheesecake","amount":1,"price":3.75}]}""",
                response.content
            )
            assertEquals(HttpStatusCode.OK, response.status())
        }
    }

}

However, when writing withTestApplication with the embeddedServer approach, since everything is declared directly inside the main() function, you can't test the entire application at once. Instead, you can test at the Application extension function level — like configureRouting(), registerCustomRouting(), and registerOrderRouting() that I defined above. 

One thing to watch out for here: configureRouting can be tested on its own, but registerOrderRouting and registerCustomRouting cannot. That's because if you plug them in directly and run them, the ContentNegotiation registered in the Application will be missing, causing issues with the serialization process and making the tests fail. So you need to register both configurations together when testing.

With testing set up like this, using ktor makes it look easy to test by module and by configuration.
Honestly, whether you use embeddedServer or HOCON, it's the same in that you create one Application extension function and test with it, so just use whichever you prefer.

 

댓글

Comments

Develop/git-github

git 종종 사용하지만 까먹는 명령어들 | git commands I occasionally use but keep forgetting

+ 조금씩 추가할 예정이미 commit한 메세지 author 변경git rebase -i HEAD~N # 원하는 수정 커밋 범위 설정# 원하는 커밋 pick > e 로 수정 후git commit --amend --author="jyami-kim " # --amend로 author 변경git rebase --continue # 다음 rebase 진행 branch upstream 변경local branch가 remote branch를 추적하도록 git branch --set-upstream-to=origin/feature/TM-5644 feature/TM-5644 tag 추가git tag tag-namegit push origin tag-name git local 설정git config --local u..

git 종종 사용하지만 까먹는 명령어들 | git commands I occasionally use but keep forgetting

728x90

+ 조금씩 추가할 예정

이미 commit한 메세지 author 변경

git rebase -i HEAD~N # 원하는 수정 커밋 범위 설정

# 원하는 커밋 pick > e 로 수정 후

git commit --amend --author="jyami-kim <mor2222@naver.com>"   # --amend로 author 변경

git rebase --continue # 다음 rebase 진행

 

branch upstream 변경

local branch가 remote branch를 추적하도록

 git branch --set-upstream-to=origin/feature/TM-5644 feature/TM-5644

 

tag 추가

git tag tag-name
git push origin tag-name

 

git local 설정

git config --local user.email "mor2222@naver.com"
git config --local user.name "jyami-kim"

+ Will be updated gradually

Changing the author of an already committed message

git rebase -i HEAD~N # 원하는 수정 커밋 범위 설정

# 원하는 커밋 pick > e 로 수정 후

git commit --amend --author="jyami-kim <mor2222@naver.com>"   # --amend로 author 변경

git rebase --continue # 다음 rebase 진행

 

Changing branch upstream

Make a local branch track a remote branch

 git branch --set-upstream-to=origin/feature/TM-5644 feature/TM-5644

 

Adding a tag

git tag tag-name
git push origin tag-name

 

Git local configuration

git config --local user.email "mor2222@naver.com"
git config --local user.name "jyami-kim"

댓글

Comments

Develop/DevOps

규모 확장 시스템 설계 기본 | Fundamentals of Designing Systems for Scale

1. 단일 서버 DNS : 도메인 이름을 이용해 웹사이트에 접속한다. DNS에 질의하여 IP로 변환하는 과정이 필요하다. http 요청을 보내고 클라이언트는 응답을 받는다2. 데이터베이스 데이터베이스 : 웹/모바일 트래픽 처리 서버 (웹 계층)와 데이터베이스 서버 (데이터 계층) 분리를 시도한다.3. 로드밸런서 로드밸런서 : 부하 분산 집합에 속한 웹서버들에게 트래픽 부하를 고르게 분산한다 데이터베이스 : 다중화로 성능과 안정성을 보장한다 (master는 쓰기, slave는 읽기)4. 캐시 캐시 : 캐시를 이용해 서버의 요청이 보다 빨리 처리될 수 있게 한다. SOPF가 되지 않게 분산한다5. 콘텐츠 전송 네트워크 (CDN) CND : 정적 콘텐츠(이미지, 비디오, CSS, ..

규모 확장 시스템 설계 기본 | Fundamentals of Designing Systems for Scale

728x90

1. 단일 서버 
   DNS : 도메인 이름을 이용해 웹사이트에 접속한다. DNS에 질의하여 IP로 변환하는 과정이 필요하다. 
   http 요청을 보내고 클라이언트는 응답을 받는다

2. 데이터베이스
   데이터베이스 : 웹/모바일 트래픽 처리 서버 (웹 계층)와 데이터베이스 서버 (데이터 계층) 분리를 시도한다.

3. 로드밸런서
   로드밸런서 : 부하 분산 집합에 속한 웹서버들에게 트래픽 부하를 고르게 분산한다
   데이터베이스 : 다중화로 성능과 안정성을 보장한다 (master는 쓰기, slave는 읽기)

4. 캐시
   캐시 : 캐시를 이용해 서버의 요청이 보다 빨리 처리될 수 있게 한다. SOPF가 되지 않게 분산한다

5. 콘텐츠 전송 네트워크 (CDN)
   CND : 정적 콘텐츠(이미지, 비디오, CSS, JavaScript)는 웹 서버대신 CDN으로 성능을 보장한다

6. 무상태(stateless) 웹 계층
   웹서버 : 무상태 웹 계층을 갖게 함으로써 자동 규모 확장(autoScaling)이 가능하다
   공유 저장소 : 웹서버를 무상태 웹 계층으로 전환하면서, 필요한 상태정보들은 공유 저장소에 저장한다.

7. 데이터 센터
   로드밸런서 : 데이터 센터를 이용해 가용성을 높이고, 전 세계 어디서도 쾌적하게 사용이 가능하다. 지리적 라우팅을 이용하여 사용자의 위치에 따라 가장 가까운 위치의 데이터 센터로 안내한다.

8. 메시지 큐
   메시지 큐 : 서버간 결합을 느슨하게 하여 (loosely coupled) 규모 확장성이 보장되는 안정된 애플리케이션 구성이 가능하게 한다.
   
9. 로그, 메트릭 그리고 자동화
   도구 : 로그, 모니터링, 메트링, 자동화는 규모가 큰 서비스 관리에 용이하다

10. 데이터베이스의 규모 확장
    데이터베이스 : 샤딩으로(수평적 확장 = 서버증설) DB부하를 줄인다.

관련 책 : 가상 면접 사례로 배우는 대규모 시스템 설계 기초


   

1. Single Server 
   DNS : You access a website using a domain name. A process of querying DNS to resolve it into an IP address is needed. 
   You send an HTTP request and the client receives a response.

2. Database
   Database : We separate the server that handles web/mobile traffic (web tier) from the database server (data tier).

3. Load Balancer
   Load Balancer : Evenly distributes traffic load across web servers in the load-balanced set.
   Database : Replication ensures performance and reliability (master handles writes, slave handles reads).

4. Cache
   Cache : Uses cache so that server requests can be processed faster. Distribute caches to avoid becoming a SPOF.

5. Content Delivery Network (CDN)
   CDN : Static content (images, videos, CSS, JavaScript) is served through a CDN instead of web servers to ensure performance.

6. Stateless Web Tier
   Web Server : By making the web tier stateless, auto-scaling becomes possible.
   Shared Storage : When converting web servers to a stateless web tier, the necessary state information is stored in shared storage.

7. Data Centers
   Load Balancer : Using data centers improves availability and enables a smooth experience from anywhere in the world. GeoDNS routing directs users to the nearest data center based on their location.

8. Message Queue
   Message Queue : By loosely coupling servers, it enables building stable applications with guaranteed scalability.
   
9. Logging, Metrics, and Automation
   Tools : Logging, monitoring, metrics, and automation make it easier to manage large-scale services.

10. Database Scaling
    Database : Sharding (horizontal scaling = adding more servers) reduces the database load.

Related Book: System Design Interview – An Insider's Guide


   

댓글

Comments

Dev Book Review

Redis 운영 관리

너무 유용해서 정리 중 관련 책 : www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9788968486814 Redis 운영 관리 - 교보문고 『Redis 운영 관리』는 Redis의 특징과 함께 어떻게 운영하고 관리해야 하는지를 알아보는 책이다. Redis 복제 모델과 복제시 주의해야 할 사항을 학습한다. 저자는 현장에서 얻은 노하우와 실무 팁 www.kyobobook.co.kr 1. Redis의 이해 문서 : redis.io/ 소스 : github.com/redis/redis 커맨드 : redis.io/commands Redis의 주요 특징 key-value 스토어 : 단순 스트링에 대한 Key/Value 구조를 지원..

Redis 운영 관리

728x90

너무 유용해서 정리 중

관련 책 : www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9788968486814

 

Redis 운영 관리 - 교보문고

『Redis 운영 관리』는 Redis의 특징과 함께 어떻게 운영하고 관리해야 하는지를 알아보는 책이다. Redis 복제 모델과 복제시 주의해야 할 사항을 학습한다. 저자는 현장에서 얻은 노하우와 실무 팁

www.kyobobook.co.kr

 

1. Redis의 이해

 

Redis의 주요 특징

  • key-value 스토어 : 단순 스트링에 대한 Key/Value 구조를 지원한다.
  • 컬렉션 지원 : List, Set, Sorted Set, Hash 등의 자료구조를 지원함
  • Pub/Sub 지원 : Publish/Subscribe 모델을 지원한다 (서버간의 통지에 유용함)
  • 디스크 저장 (Persistent Layer) :
    • 현재 메모리 상태의 스냅샷을 남기는 기능 'RDB' / 지금까지 실행된 업데이트 관련 명령어의 집합 'AOF'
    • RDB : 메모리 내용을 저장하는 기능 외에는 아무것도 지원하지 않는다. (데이터베이스가 아니다)
    • AOF (Append Only File) : set / del 등의 업데이트 관련 명령어를 그대로 기록함
  • 복제 (replication) : 다른 노드에서 해당 내용을 복제할 수 잇는 마스터/슬레이브 구조를 지원
  • 빠른 속도 : 이상의 기능을 지원하면서도 초당 100,000 QPS (Queries Per Second) 수준의 높은 성능을 자랑

 

Redis와 Memcached 비교

기능 Redis Memcached
속도 초당 100,000QPS 이상 초당 100,000QPS 이상
자료구조 Key-Value, List, Hash, Set, Sorted Set Key-Value
안정성 특성을 잘못 이해할 경우 프로세스 장애 발생 장애 거의 없음
응답 속도의 균일성 균일성이 떨어질 수 있음 전체적으로 균일

Memcached에 저장소의 개념이 추가된 것이 Redis 
저장소의 개념 == [RDB, AOF, Replication]

응답속도의 균일 성부분이 문제가 되는 이유 : 두개의 메모리 할당 구조가 다르기 때문
- Redis : jemalloc - free > 메모리 프래그멘테이션(fregmentation) 할당 비용 때문에 응답 속도가 느려진다.
- Memcached : slab 할당자 

더보기

slab 할당자

내부 단편화 문제를 해결하기 위함 (page 단위 4kb (4096byte) - 할당 받으려는 것 16바이트(16byte) = 4080byte 낭비)

솔루션
- 자주 쓰는 메모리 패턴을 정의한 후 미리 할당
- 해당 페턴에 대한 메모리 할당 요청이 있으면 메모리 할당
- 해당 패턴으로 메모리를 해제하면 우선 그대로 유지 (또 다시 해당 패턴으로 할당 요청을 할 가능성이 높음)

kmem_cache_s 구조체 : 하나의 캐시를 나타냄
keme_list3 구조체 : kmem_cache_s들의 모음. slab 관리
슬랩은 사용자에게 할당할 object들이 저장되어있는 풀구조이다.

커널이 시스템을 초기화 할 때 kmem_cache_init 함수를 통해 자주 사용되는 커널의 오브젝트들의 크기를 고려해 사용목적에 따라 캐시를 생성한다. 32, 64, 128, 256, 512, 1,024, 2,048, 4,096, 8,192, 16,384, 32,768, 65,536, 131,072 byte (페이지 단위 관리보다 내부 단편화를 줄일 수 있다)

 

2. Redis 운영과 관리

핵심 1 : Redis는 싱글 스레드다

싱글 스레드이기때문에 시간이 오래 걸리는 Redis 명령을 호출하면, 명령을 처리하는 동안에는 Redis가 다른 클라이언트의 요청을 처리할 수 없다.

1. 서버에서는 keys 명령을 사용하지 말자

keys 명령 : 원하는 패턴에 매칭되는 키들을 가져오는 명령어이다.
그러나 이걸 사용하면 장애로 이어질 가능성이 높다. 데이터 양이 늘어날 수록 해당 명령의 속도가 느려진다. 실제로 Redis 매뉴얼에 쓰지 말라고 되어있다.

redis.io/commands/keys

 

KEYS – Redis

Returns all keys matching pattern. While the time complexity for this operation is O(N), the constant times are fairly low. For example, Redis running on an entry level laptop can scan a 1 million key database in 40 milliseconds. Warning: consider KEYS as

redis.io

Warning
: consider KEYS as a command that should only be used in production environments with extreme care. It may ruin performance when it is executed against large databases. This command is intended for debugging and special operations, such as changing your keyspace layout. Don't use KEYS in your regular application code. If you're looking for a way to find keys in a subset of your keyspace, consider using  SCAN or SETS.

디버깅이나 스페셜한 명령어(keyspace layout 변경하기)로 의도된 것이라 보통의 어플리케이션 코드에서는 사용하면 안된다. 웬만하면 SCAN이나 SETS 명령어를 사용하는걸 고려해보아라.

모든 Key를 대상으로하는 명령어라서 그렇다.

2. flushall/flushdb 명령을 주의하자

db : 가상의 공간을 분리할 수 있는 개념   
- flushdb: db하나의 내용을 통째로 지우는 것
- flushall : 모든 db의 내용을 모두 지우는 것

flushall 명령은 전체 데이터를 다 지우며 keys명령처럼 많은 시간이 필요하다 (memcached는 순식간에 모든 데이터가 지워진다) - 동작방식이 다르다

아이템 개수에 비례해서 시간이 걸린다
실제 데이터를 일일히 삭제하는 로직으로 구현이 되어있다 : 지우는 속도가 O(n)

Memecached의 flush_all이 빠른 이유

Memcached에서는 실제로 데이터를 삭제하지 않는다.
해당 명령어가 실행된 시간만 기록하고, 이보다 이전에 저장된 key는 get명령을 통해서 접근할 때 없다고하면서 실제로는 지운다. (oldest_live 플래그만 세팅 : 이보다 먼저 생성된 key는 없다)

 

핵심2: Redis Persistent

Reids Persistent : Redis 데이터를 디스크로 저장할 수 있다 > 디스크에 저장된 데이터 기반으로 복구가 가능하다

1. RDB

현재 메모리에 대한 덤프를 생성하는 기능.

fork를 이용해서 자식 프로세스를 생성한다. > 현재 메모리 상태가 복제된 자식프로세스를 기반으로 데이터를 저장한다 (지속적인 서비스가 가능하다) - 근데 이렇게 하면 최악의 상황에 메모리가 2배가 필요할 것 같다.

- SAVE : 모든 작업을 멈추고 현재 메모리 상태에 대한 RDB파일 생성 : 싱글 스레드 이므로 아무 작업도 수행할 수 없음
- BGSAVE : 백그라운드 SAVE : fork 작업을 통해 자식 프로세스에서 파일을 저장한다.

#redis.conf
dbfilename dump.rdb

 

2. AOF

AOF : Append Only File
현재 수행해야 할 명령을 미리 저장해두고, 장애가 발생하면 AOF 기반으로 복구

1. 클라이언트가 Redis에 업데이트 관련 명령 요청
2. Redis가 해당 명령을 AOF에 저장
3. 파일 쓰기가 완료되면 실제로 해당 명령을 실행해서 메모리의 내용을 변경함.

AOF는 기본적으로 사용안함으로 되어있어 변경해주어야한다.

#redis.conf
appendonly yes # default no
appendfilename appendonly.aof # 파일이름 설정
appendfsync everysec # 디스크 동기화를 얼마나 자주할 것인가 (always, everysec, no)

 

AOF와 RDB의 우선순위 : 최신 데이터를 더 많이 가진 파일 - AOF 
RDB는 스냅샷인 반면, AOF는 매 작업마다 디스크에 기록을 남겨서 모든 데이터가 남아있다.

 

참고 : 레디스 프로토콜

*키워드개수\r\n
[키워드개수만큼 반복]
$키워드크기\r\n
키워드\r\n

 

 

3. Redis가 메모리를 두배로 사용하는 문제

장애 원인 : RDB저장시 fork를 사용하기 때문에.

COW (Copy on Write) : 실제로 변경이 발생한 부분만 차후에 복사한다. (그러나 redis는 부분 write가 많다)

참고 : redisgate.kr/redis/configuration/copy-on-write.php

 

Redis Copy-on-Write

copy-on-write Redis Copy-on-Write 분석 Redis Copy-on-Write 분석 개요 槪要 Outline 레디스 서버의 메모리 사용량은 실 데이터 크기에 관리 메모리(overhead)를 더해야 한다.   그리고 Copy-on-Write로 인한 추가 메모

redisgate.kr

참고 : 대용량 메모리와 Redis

Redis는 싱글 스레드임 : 멀티 코어를 활용하기 위해 여러 개의 Redis 서버를 한 서버에 띄우는 것이 성능 면에서 좋다.

Core4개 + 메모리 32GB 장비 : 프로세스별로 6G 할당하기
- 평상시 : 6 + 6 + 6 + 6 = 24GB
- fork 복제시 : 6 + 6 + 6 + 6 + (6 = 자식) = 32GB

 

4. Redis장애 : Read 성공 Write 실패

Redis 기본 설정 : RDB저장 실패 => 장비 이상으로 판단 => Write 명령처리하지 않음 
lastbgsave_status = REDIS_ERR : Write 관련 요청 모두 무시

Heartbeat체크는 읽기 관련 명령을 이용해 검사하기 때문에 문제 발생 가능.

RDB 생성 실패의 경우
- RDB를 저장할 수 있을 정도의 디스크 여유 공간이 없는 경우
- 실제 디스크가 고장 난 경우
- 메모리 부족으로 인해 자식 프로세스를 생성하지 못한 경우
- 누군가 강제적으로 자식 프로세스를 종료시킨 경우

해결 방법
1) 해당 상황이 맞는지 확인하기 : set 명령어 사용
2) info 명령어 사용 : rdb_last_bg_save_status:ok 인지 확인
3) 정책 결정 : config set stop-writes-on-bgsave-error no

 

3. Redis 복제

1. Redis 복제 모델

redis는 마스터 / 슬레이브 형태의 복제 모델 제공
-> 이때 슬레이브가 다른 장비의 마스터로도 동작할 수 있게도 할 수 있음 (M-S-S)

1. 명령어 사용 : slaveof <ip> <port>
2. redis.conf 수정 : slaveof <master ip> <master port>

redis-cli로 접속 후 info 명령어 혹은 info replication 명령어를 사용한다.

6666 - 슬레이브 / 3333 - 마스터

 

2. Redis 복제 과정

1. slave에서 slaveof 명령어를 이용해 마스터 서버를 설정한다.
2. 마스터 서버가 설정되면 replicationCron에서 현재 상태에 따라 connectWithMaster를 호출한다.
3. 마스터는 복제를 위해 RDB를 생성한 후 슬레이브에 전송한다. 
4. 슬레이브는 RDB를 로드하고, 나머지 차이에 대한 명령을 마스터에서 전달받아 복제를 완료한다.

RDB 기준 복제 순서 (full synchronization)
1. 마스터는 자식 프로세스를 시작해 백그라운드로 RDB파일에 데이터를 저장합니다.
2. 데이터를 저장하는 동안 마스터에 새로 들어온 명령들은 처리 후 복제버퍼에 저장됩니다.
3. RDB 파일 저장이 완료되면, 마스터는 파일을 복제서버에게 전송합니다.
4. 복제서버는 파일을 받아 디스크에 저장하고, 메모리로 로드합니다.
5. 마스터는 복제버퍼에 저장된 명령을 복제서버에게 전송합니다.

이때 복제중 끊어지게 되면 backlog-buffer에 데이터가 저장된다. 이후 다시 연결되었을때 이 buffer에서부터 동기화가 이루어진다.
만약 buffer가 넘쳤을때는 full synchronization을 진행한다

 

3. Redis 복제 사용시 주의 사항

주의점 1. slaveof no one을 기억하자

슬레이브는 마스터의 상태를 지속적으로 감시하면서 바뀌는 내용을 계속 전달받는다. 
- 연결 상태 이상이 생기는 경우 : 재 연결이 되면 rsync
- 마스터에 장애가 발생하여 마스터에 데이터가 없는경우 : 슬레이브의 모든 내용이 사라진다.

슬레이브 복제를 진행할 때 emptyDb() 호출로, 현재 자신(슬레이브)의 데이터를 모두 삭제하고 마스터와의 싱크를 맞추려하기 때문이다.

slaveof no one : 더이상 슬레이브로 동작하지 않도록 설정하는 것

주의점 2. 복제 시에 무조건 RDB를 백그라운드로 생성한다는 것을 주의하자

RDB를 사용한다 == 메모리를 두배로 사용할 가능성이 있다. > 설정을 꺼둠
그치만 복제를 사용한다는 것 자체가 fork를 사용해서 RDB를 생성한다는 것 : 하나의 프로세스가 너무 많은 메모리를 사용하지 않도록 나누는 과정이 필요하다.

 

4. Redis 복제를 이용한 실시간 마이그레이션

1. 데이터 이전을 위한 새로운 redis 인스턴스 실행
2. 새로운 Redis 인스턴스를 기존 마스터의 슬레이브로 설정 (slaveof) - 복제 완료
3. 새로운 장비의 slave-read-only 설정을 끈다.
4. 클라이언트들이 새로운 redis 인스턴스를 마스터로 인식하도록 설정을 바꾼다.
5. slave no one 명령어 : 기존 마스터와의 연결 종료 (완전한 마이그레이션 이후)
6. 기존 장비 제거

 

4. Redis HA와 Sentinel

1. Redis HA와 Sentinel 구성

redis master에 장애가 발생하면 sentinel은 슬레이브 중 한대를 선택해서 마스터로 승격시킨다 (내부적으로 투표과정을 거친다)
이때 sentinel이 client에 pub/sub으로 master 변경을 통지한다. 
- client(sub) -> sentinel(pub)

 

2. Sentinel이 장애를 판별하는 방법

기본 : PING 명령어의 응답을 활용해서 판단함. (PONG이 안와도 바로 장애로 판단하지는 않는다)
- SDOWN : Subjectively Down : 해당 서버의 장애를 주관적으로 판단 
- ODOWN : Objectively Down : 해당 서버의 장애를 객관적으로 판단 -> 진짜 장애 : Failover 진행한다.

sentinel이 여러대 있을 때 Quorum(쿼럼) 값 이상의 센티널에서 SDOWN으로 판단해야 ODOWN이 된다.
- 주로 Sentinel 장비 수를 홀수로
- 쿼럼 값을 해당 장비수의 과반으로 설정하는 것이 좋다
   > 대부분의 설명에서 Sentinel을 3대를 띄우고 쿼럼 값을 2정도로 주는 것으로 얘기를 하긴 한다.

SDOWN 판별 : 해당 서버의 last_avail_time과 현재 시간의 차이가 설정된 down_after_period 값보다 클 때
ODOWN 판별 : SDOWN일 경우에만 쿼럼 값을 체크하며, 이 이상일때 ODOWN

 

3. Sentinel이 마스터로 승격할 슬레이브를 선택하는 방법

sentinelSelectSlave 함수로 결정

1. SDOWN, ODOWN, DISCONNECT 된 상태의 슬레이브는 제외
2. last_avail_time이 info_validity_time 보다 작으면 제외
3. info_refresh 값이 info_validity_time 보다 작으면 제외
4. master_link_down_time 이 max_master_down_time 보다 크면 제외
5. 남은 후보들 중에서 slave_priority가 높은 슬레이브가 우선적으로 선택

slave_priorirty 값은 redis.conf 파일을 조정하여 선출시의 가중치에 영향을 줄 수 있다. 
- 0으로 설정하면 해당 슬레이브는 절대로 마스터 승격이 안된다.

 

4. Sentinel 설정과 사용

# sentinel monitor <클러스터 명> <마스터 IP> <마스터 Port> <쿼럼값>
sentinel monitor resque 127.0.0.1 2001 2 

# 다운으로 인식하는 시간
# sentinel down-after-milliseconds <클러스터 명> <시간 miliseconds>
sentinel down-after-milliseconds resque 3000 

# sentinel failover-timeout <클러스터 명> <시간 miliseconds>
sentinel failover-timeout resque 900000

# sentinel can-failover <클러스터 명>
sentinel can-failover resque yes # failover 여부 설정

# 마스터 승격후 몇개의 슬레이브가 싱크를 해야 클라이언트에 알려줄 것인지
# sentinel parallel-syncs <클러스터 명> <sync할 slave 숫자>
sentinel parallel-syncs resque 1 

psubscribe 명령을 이용해 통지를 받는다. (pub/sub)

+switch-master 감지 
<클러스터 명> <이전 마스터 IP> <이전 마스터 Port> <새 마스터 IP> <새 마스터 Port>

 

5. Redis 모니터링

python 스크립트 : info 명령어를 활용한 스크립트. (python-redis 모듈)

Percona Cacti 플러그인 : www.percona.com/doc/percona-monitoring-plugins/LATEST/cacti/redis-templates.html

Redis-stat : github.com/junegunn/redis-stat

redmon : github.com/steelThread/redmon

 

추가 참고하기 좋은 사이트 - redisgate.kr/redisgate/ent/ent_intro.php

 

Redis-Enterprise Introduction

ent_intro 레디스 엔터프라이즈 레디스 엔터프라이즈 서버의 주요 기능 I.   Redis + SQL : 데이터 활용 획기적 향상 II.  Active-Active 이중화 : 진정한 고가용성을 실현 III. 메모리 한계 극복 IV. 기타 추

redisgate.kr

 

 

 

댓글

Comments

Daily/About Jyami

공채없이 카카오 개발자 취준기 | How I Got a Developer Job at Kakao Without Open Recruitment

오아... 이걸 이제야 쓰다니.. 계속해서 고민하다가 드디어 글을 완성해 보려한다. (그래도 카카오 갔는데.. 네이버 후기가 블로그 조회수 가장 높은게 맞나 싶어서ㅋㅋㅋㅋ) 6월 중순 네이버 정직원 전환 면접에서 떨어진 이후 나는 취준을 시작하게 되었다. jyami.tistory.com/116 네이버 백엔드 인턴십 후기올해 3월부터 6월까지 약 3개월간 했던 네이버 인턴십 후기를 작성하려 한다. 계속 써야지 써야지 하면서 미루다가 인턴십이 끝난 지 거의 2달가량이 돼서 사실 시간이 꽤 많이 지났지만, 아무래jyami.tistory.com네이버 인턴십을 준비할 때는 알고리즘 준비, 면접준비 하나도 없이 그냥 인턴십에 합격했었다. 그래서 회사 첫 면접이 저 네이버 인턴십 면접이었기 때문에 네이버를 갔다면 취..

공채없이 카카오 개발자 취준기 | How I Got a Developer Job at Kakao Without Open Recruitment

728x90

오아... 이걸 이제야 쓰다니.. 계속해서 고민하다가 드디어 글을 완성해 보려한다. (그래도 카카오 갔는데.. 네이버 후기가 블로그 조회수 가장 높은게 맞나 싶어서ㅋㅋㅋㅋ)
 6월 중순 네이버 정직원 전환 면접에서 떨어진 이후 나는 취준을 시작하게 되었다.

jyami.tistory.com/116

 

네이버 백엔드 인턴십 후기

올해 3월부터 6월까지 약 3개월간 했던 네이버 인턴십 후기를 작성하려 한다. 계속 써야지 써야지 하면서 미루다가 인턴십이 끝난 지 거의 2달가량이 돼서 사실 시간이 꽤 많이 지났지만, 아무래

jyami.tistory.com

네이버 인턴십을 준비할 때는 알고리즘 준비, 면접준비 하나도 없이 그냥 인턴십에 합격했었다. 그래서 회사 첫 면접이 저 네이버 인턴십 면접이었기 때문에 네이버를 갔다면 취준 없이 취뽀를 할 수 있다는 행복회로를 엄청 돌렸었다ㅋㅋㅋ. 그치만 현실은 아쉽게 전환이 되지 못했고ㅠ 진짜 취준하네..? 이러고 취준을 하게 되었다ㅋㅋㅋ

결론만 놓고 보자면 취준을 그렇게 장기간 신경쓰면서 한 것 같진 않지만, 짧다면 짧은 1달간의 취준기를 써보려한다.

 

1. 가고싶은 회사

목표하는 회사 정하기

나는 모든 대학생들이 취준을 시작할 때 본인이 가고싶은 회사에 대한 기준을 세우는게 가장 중요하다고 본다. 공기업, 스타트업, 대기업, IT기업, 외국계 회사 등 너무 다양한 회사가 있고, 각각의 회사마다 신입사원을 뽑는 기준이 다르기 때문이다. 또한 본인이 희망하는 회사가 원하는 인재상부터 시작해서 필요한 기본 스펙 (어학, 자격증 등)부터 각 회사마다의 채용 절차가 전부 다르기 때문에 미리 준비하기 위해서이다. 

예시로 내가 만약 삼성을 가고 싶다 생각했으면, 우선적으로 삼성에서 그동안 나왔던 코딩테스트 기출문제를 풀어보고 채용 절차에 있는 직무 적성검사도 공부할테고, 면접을 간다했을 때 면접의 순서라던지 각 면접의 특징에 맞춰서 준비를 했을 것이다. 이런 채용 절차는 보통 그 회사의 채용 안내 및 전형 프로세스에 자세하게 나와있다. (구글링을 생활화 합시다) 이런 채용 절차를 보면 본인이 현재 가진 스펙을 바탕으로 객관화를 한 후에 부족한 부분을 준비할 수 있다.

본인이 가고싶은 회사를 정했다면 앞으로 길고 긴 채용절차와 그에 맞는 준비를 할 예정일 것이다. 그 고된 과정을 거쳐서도 본인이 가고싶은 회사여야지 합격 발표가 났을때 그만큼 기쁨은 더 될 것이다. 따라서 나는 누구든 꼭 본인이 가고싶은 회사를 우선적으로 선택하고, 그 선택에 집중해서 준비를 하는게 중요하다고 생각한다.

IT 회사가 가고싶어요..

그래서 나는 "개발자로 성장을 할 수 있는 곳" 이라는 키워드를 기반으로 IT회사 혹은 외국계 IT회사를 생각을 했고, 당시 그래서 카카오, 하이퍼 커넥트, 네이버, SAP 이 네 곳에 지원서를 제출했다. (외에도 관심있는 IT회사는 많았지만 당시 내가 관심있어하는 공고가 이 네 곳이었다)

그래서 공통적으로 IT기업이라는 특징을 가진 이 네 곳에서 요구하는 인재상은 사실 비슷비슷 했다. "개발을 좋아하는, 잘하는 개발자"가 핵심 키워드였다. 좀 더 자세히 보면, java를 잘 이해하는 사람, computer science 지식을 잘 알고있는 사람.. 등등 원하는 기술 역량이 잡 디스크립션에 상세하게 적혀있다.

개발자를 채용하는 회사 입장에서 이렇게 물어보는 것 같았다. 앞으로 개발 공부 더 많이 해야할거고, 넌 그동안 개발 서적이나 여러 개발 협업 경험, 전공 지식에 대한 깊은 이해, 본인이 사용하는 주 언어에 대한 이해는 있어야 우리와 함께 할 수 있는데 준비 되어있니? 그렇다. 학부생으로 개발에 어느정도 관심을 가지고 있었고, 개발 능력 향상을 위해 본인이 그동안 어떤 노력과 공부를 했는지, 그 공부가 정확하게 되어있는지를 물어보고 있었다.

그리고 현재 상태의 나는 요구되는 그 역량을 그동안 내가 학부생활 동안 했던 공부 내용이나 프로젝트내용, 기술적인 관심으로 충분히 어필을 할 수 있을거라는 생각이 들었다.  그리고 잡 디스크립션에서 내가 맞지 않는 부분이 있더라도 내가 현재 가지고 있는 기술스택을 충분히 깊게 공부했다는 점을 어필할 생각이었다.

이건 팁인데 본인이 앞으로 무엇을 공부해야할지 모르겠으면, 본인이 가고싶은 회사의 개발역량 지원 자격을 보면 도움이 된다. 나같은 경우 Kotlin 서버 개발 경험이 없는데, 만약 3학년때 이 공고를 봤다면 Kotlin을 공부라는 새로운 공부거리를 찾을 수 있었을 것이다. 그리고 알고있는 내용이라고 하더라도 무조껀 본인이 가능한 최대한 깊게 공부하면 좋다. 예를들어 java 기반 서버 개발 경험이라고 하면, 내 블로그 몇몇 글을 보면 알겠지만 학부에서 배운 자바 수업 외로도 이펙티브 자바와 같은 책을 읽거나, 제네릭과 같이 이해가 안되는 내용이 있을때 더더 찾아보면서 성장하자. 

 

2. IT 기업의 지원 방식

내가 가고싶었던 회사는 주로 IT기업이었고, 그중에서도 카카오, 네이버를 가고싶었다. 그래서 해당 회사에 신입으로 입사를 할 수 있는 방법을 많이 접하고 찾아봤던 것 같다. 내가 볼 때 이 두 회사의 지원 방식은 크게 3가지가 있다.

  • 공채 (상시 채용)
  • 수시 채용
  • 인턴 (상시 채용 / 수시 채용)

공채

공채는 많은분들이 아실 것이라 생각한다. 공채는 주로 상반기 하반기 공채로 나누어져있으며, 한번에 많은 신입 개발자를 뽑는 제도이다. 아무래도 공채는 카카오, 네이버 외에도 많은 회사에서 신입사원을 뽑을 때 사용하는 방법이라 다들 잘 알고있으리라 생각된다.

그러나 IT기업임을 감안하여 공채 특징을 짚어보자면

  • 정말 많은 지원자
  • 코딩테스트가 어려움
  • 면접은 주로 컴퓨터 공학 전공 기초 내용 위주의 질문으로
  • 면접에서 N : N (지원자 : 면접관)으로 볼 수 있음
  • 바로 정규직으로 입사
  • 회사에서 제공하는 지원기간에 맞춰서 본인의 취업 스케줄을 조정해야함
  • 입사한다면 동기들이 있음
  • 공채 합격후 워크숍 기간을 갖고 생활
  • 어떤 팀에 갈지 모른다

위에 코딩테스트가 어렵다는 특징이 있는데, 실제로 공채를 준비하는 대학생 분들 중에 해당 기업에서 나온 기출문제를 바탕으로 N년간 대비하는 분들도 보았다. 실제로 카카오 공채는 프로그래머스에서 진행을 하는데, 프로그래머스에서 공채 코딩테스트 문제 분석이라는 강의를 파는 모습도 본 것 같다 (18년도인가..)

또 이건 개인적으로 공채 공고를 보면서 느낀건데, 공채 공고에서는 기술 역량 자격이 적혀져있지 않다. 자바, 씨, 파이썬 언어 상관없이 본인이 해당 개발 분야에 대해 자신이 있고, 컴퓨터 공학적 능력이 뛰어나다면 신입개발자로 채용하겠다는 느낌이 들었다.

나는 공채는 지원하지 않았다. 개인적으로 코딩테스트에 자신이 없었다. 나름대로 코딩테스트 대비를 하긴 했지만, 많은 공채 지원자들 사이에서 최상위권이 될 수 있을까라는 의문이 있었기 때문이다. (그래도 리트코드 easy~medium 정도는 푼다) 그리고 공채에서는 많은 지원자들이 몰리는 만큼 내 이력을 꼼꼼히 봐주실까? 라는 의심도 있었다. 그리고 가장 중요했던 이유는 6월에 시작한 취준이었는데, 공채를 보려면 9월까지는 기다려야했고 그 사이에도 충분히 회사에 지원하고 합격할 수 있을 것이라고 생각했다.

그렇지만 바로 정규직이 될 수 있다는 안정감과 함께 입사하는 동기들이 있다는 점이 공채의 가장 큰 장점이자 내 입장에서 부러운 점이었다.

 

수시채용

내가 카카오에 지원한 방식이다. 수시 채용의 경우에는 모르는 대학생도 있을 것으로 보인다. 수시 채용은 각 회사의 채용사이트에 있는 개발자 공고에 맞춰서 지원하는 방식이다. 여기 보면 (경력) 이렇게 써져있다보니 나같은 신입은 지원하지 못하는 공고아니야? 라고 생각할 수 있는데, 채용사이트를 잘 뒤져보면 경력무관, 혹은 신입 개발자에 대한 수습기간 설명이 적혀있는 경우가 있다.

아무래도 경력 개발자를 위한 공고가 주 목적인 사이트는 맞지만, 팀이 제시한 요구 스펙을 맞춘 신입 개발자가 지원을 하는 경우도 고려한 공고가 가끔 있다. 따라서 수시 채용을 노린다면 본인이 원하는 회사의 채용사이트를 주기적으로 확인하자.

IT기업 개발자 수시 채용 특징을 신입 개발자 입장에서 짚어보자면

  • 팀의 분위기를 고려한 채용
  • 팀이 요구한 기술 스펙에 맞는 인재 선호
  • 신입 지원시 회사에 따라 3개월의 수습 기간을 가질 수 있음
  • 수습 기간이 있을 경우 평가에 따라 계속 일 할 수 있을지 없을지 여부가 정해짐 (3개월 계약직)
  • 코딩 테스트는 팀바팀
  • 면접에서 컴퓨터 공학 지식 내용 물어봄
  • 면접에서 지원서에 쓴 내용을 바탕으로 본인 경험기반으로 깊게 물어봄
  • 면접에서 1 : N (지원자 : 면접관)
  • 채용일정 전반적으로 본인의 상황에 맞게 조정가능

수시 채용의 경우에는 지원자가 나 혼자이다보니, 내 지원서를 보고 좀 더 맞춰서 질문을 하는 경향이 있었다. 또한 공채 신입정도의 실력이 되는지를 판단하기 위한 지표로 컴퓨터 공학 지식도 하드하게 물어봤었다. 코딩테스트의 경우엔 내 기준 공채 기출보다는 좀 더 쉬운 편이었으나 이는 팀바팀일 것으로 예상된다. (그러나 대부분 공채 보다는 좀 낫다..) 

그리고 수시 채용의 경우에는 공채와는 다르게 채용일정을 내 일정에 맞춰서 조정이 가능했다. 공채에서는 전달해준 일자에 면접, 코딩테스트를 딱딱 봐야한다면 수시채용의 경우는 회사 인사팀에서 메일이 와서 선호하는 시간 및 날짜를 물어본다.

하지만 신입이 위처럼 수시 채용으로 입사하는 경우에는 3개월의 수습기간을 가지는 등의 패널티가 있을 수 있는데, 내가 그런 상황이었고, 이때의 불안정성이 조금 힘들었다. 공채보다 더 긴 채용 절차를 거쳤음에도 계약직이며 동기도 없고 3개월 후에 이 회사에 없을 수도 있다는 점은 심리적으로 조금 위축이 되긴한다. 

수시 채용의 경우 내가 원하는 핏을 가진 회사와 팀을 지원자 본인이 선택하고, 인터뷰를 보면서 나에게 집중된 채용절차를 밟을 수 있다는 점이 장점이다. 나는 이런 점이 마음에 들어서 수시 채용을 선택했다.

 

인턴 (상시 채용 / 수시 채용)

IT기업에서 인턴 채용도 하고있다. 그렇지만 인턴이 금턴이라는 말이 생길만큼 역시 쉽지 않다고 한다. 인턴의 경우에는 상시 채용, 수시 채용 형태 모두 보았는데, 인턴 채용의 경우에는 채용 규모나 절차 모두 회사 바이 회사였다. 

예를 들어 카카오 인턴은 위에 카카오 신입 개발자 공채과 비슷하게 이루어졌지만, 네이버 인턴 상시채용은 네이버 자회사인 NBP 인턴채용 이런식으로 좀 더 작은 규모에서 상시채용이 이루어졌다. 

나같은 경우엔 이번 6월 취준시 네이버 클라우드 상시채용 인턴에 지원했었고, 1월 인턴 지원시에는 수시채용 인턴에 지원했었는데, 아무래도 인턴이다보니 위에서 말한 공채와 수시 채용의 좀더 소프트 한 버전의 지원방식이라는 느낌을 받았다. (그래도 면접준비는 빡세게 해가자)

 

3. 지원서 작성하기

2번으로 원하는 팀 혹은 회사의 지원방식을 보고 선택을 했다면 서류평가를 위한 지원서를 작성해야 할 시간이다. 서류 평가에는 보통 기업에서 제시하는 지원서와 본인 개인 레주메를 낼 수 있도록 하고있는데, 나는 주로 레주메 안에 내 활동을 담으려했으며, 지원서에는 좀 더 레주메 안의 내용을 서술적으로 풀어서 제출했다. 지원서를 쓸 때 조언하고 싶은 점은 아래와 같다.

1. 개발자 레주메의 첫인상은 개발에 관심이 많은 사람임을 알 수 있어야한다.

개발자를 뽑으니까 특히나 신입개발자를 뽑으니까 내가 내는 서류에는 개발에 대한 열정이 가득한게 좋을 것 같다 생각했다. 그래서 내가 지원서를 작성할 때는 5가지를 어필하려 노력했다.

  • 개발 서적 읽기 : 학교 지식을 넘어서 현업 개발자에 다가가기 위해 노력했다는 점 어필
  • 해커톤 : (음...그냥 재미로 나간거였지만) 모르는 스택도 필요에 따라 따르게 익혀서 사용했다는 빠른 러닝커브 어필
  • 프로젝트 / 대외활동 : 기술을 바탕으로 다른사람과 협업을 할 수 있다는 점 어필
  • 컴퓨터 공학 지식 공부 (학점) : 개발자의 기본도 성실히 이수했다. 
  • 나의 주언어 공부 (Java) : 내가 사용하는 언어를 언어의 특성에 맞게 사용이 가능하다.

이 5가지가 내 입장에서 개발자로 열정을 보여줄 수 있는 것들이었기 때문이다. 신입으로써 지원서에서 어필할 수 있는건 실제 실무보다는 본인이 개발 공부를 어떤식으로 해왔는지, 개발에 얼마나 관심이 많은지, 어떤 개발자로 성장하고 있는지 밖에 없기때문에 최대한 본인의 경험을 녹여서 일목요연하게 쓰자

실제로ㅋㅋㅋ 그래서 하이퍼 커넥트에 지원을 할 때는 신입임에도 경력직 공고에 패기롭게 넣었는데, 서류를 보시더니 졸업 예정자인지 확인하고 면접을 보게 해주셨다..ㅇ0ㅇ 그치만 코딩테스트에서 떨어졌다 (하이퍼 커넥트 코딩테스트는 되게 신기했는데, 알고리즘 테스트가 아닌 실무 코드에 대한 내용을 주로 물어봤다 그리고 영어였다)

2. 레주메는 최대한 그때그때 정리하자

대외활동이나 여러 동아리 활동을 하다보면 여러번 지원서를 쓰고 매번 이력이 업데이트된다. 그리고 각각 활동별로 깨우치는 바가 있을 것이다. 그때 깨달은 점을 꼭 정리하자. 

개인 맥북에 있는 모든 지원서
  • 1학년 멋쟁이 사자처럼 : 웹에 대한 관심, mvc 구조로 웹 개발 가능해짐. / 프론트 백엔드 뭐든 서비스 완성 위주
  • 2학년 SOPT : api 구조로 웹 개발가능해짐 / 스프링부트 학습 / 자바 숙달 / 백엔드 개발자에 관심
  • 2학년 스마일게이트 : 서버 아키텍처에 대한 이해 / 스프링 숙달
  • 3학년 자바봄 : 백엔드 개발자로써 공부해야 할 기본지식 / 클린코드 / 테스트코드 작성 / 스프링, 자바에 대한 더 깊은 이해 
  • 3학년 dsc : 개발 커뮤니티 운영
  • 4학년 인턴 및 취업준비

나같은 경우에는 대략적으로 위의 히스토리였는데, 사이사이에도 오픈핵, 네이버 핵데이 등 여러 활동 참여를 많이 했었다. 그런 활동을 하다보면 쓰는 지원서가 있는데, 그 지원서를 결국 쓰다보니 계속 조금만 고쳐서 조금만 업데이트해서 계속 사용하게 되었고 거슬러 올라가면 SOPT때의 지원서가 현재 사용한 자기소개서의 초안이 되었다. 따라서 본인의 방식으로 꼭꼭 잘 저장해두자.

3. 지원서를 쓸 때 받을 수 있는 면접 질문을 생각하고 지원서에 쓴 기술 스택은 제대로 공부하자

지원서에 작성한 기술 스택은 면접에서 물어볼 예상 질문이다. 따라서 본인이 지원서에 작성한 내용은 모두 깊게 공부해서가자. 본인이 자기소개서에 mySQL을 사용해본 경험이 있다고 적었다고 가정하자. 그러면 면접관은 mySQL의 특징부터해서, db 정규화, 트랜잭션, 인덱스, 고립레벨 등등 물어볼만한 질문이 너무 많기 때문이다. 그리고 실제로 면접이 아니더라도 본인이 공부한 기술 스택에 대한 깊이 있는 이해는 개발자라면 기본적으로 알아야하는 내용이다. 따라서 면접에서 나오는 해당 기술에 대한 질문은 정말 기초적으로 알아야하는 내용임을 암시하므로 내가 모르는 부분에 대한 질문이 나오면 땡큐라고 생각하고 바로 깊게 공부했다. (실제로 그 다음 면접에서 대답 못한 질문을 다시 대답해보라고 했었다.)

또한 면접에서 물어봤을 때 잘 대답을 못할 것 같거나, 정말 겉햝기 식으로 공부한 지식들의 경우에는 지원서에서 과감하게 뺐다. 나같은 경우에는 프론트엔드 외주 이력이 있지만, 면접에서 프론트엔드 관련해서 혹시라도 물어볼까 싶어 지원서에 해당 이력을 뺐다. 혹시라도 나한테 javascript 의 이벤트 핸들링에 대해서 설명해 보세요. 이런 질문을 하면 대답할 자신이 없었고, 그에 맞는 꼬리 질문도 대답 절대 못했다. 또한, 현재 지원서에 적은 기술 스택만 하더라도 면접 전까지 공부해야 할 내용이 산더미인데 프론트 엔드는 언제 공부하냐.. 싶었다.

4. 레주메는 읽기 쉽게 쓰자 + 앞장은 메인이다

지원서는 눈에 띄는 것도 중요하다. 해당 기업에 엄청 많은 지원자가 지원을 하는데 내용이 반복되고, 주술관계가 이상하며 읽는 사람이 이해할 수 없게 쓰면 얼마나 읽는사람이 힘들겠는가. 본인이 아무리 대단한 경험과 대단한 깨달음을 얻었다고 해도 지원서에는 최대한 요약하자 (어차피 면접에서 그 요약본을 보고 궁금해서 물어볼 것이다.) 

따라서 나는 보는 사람이 읽기 쉽게 직접 일러스트를 사용해서 그래프, 색깔처리, 볼드처리, 항목화 별짓거리를 다해서 가독성을 높이려고 했다. 그리고 주변 아는 개발자분들께 부탁을 드려서 리뷰를 받았었다.

jyami.tistory.com/8

 

Backend Developer Resume

개발자 포트폴리오, 백엔드 개발자 레주메 (update 2020.06.12)

jyami.tistory.com

또한 레주메의 경우에는 앞장이 메인이다. 대학생들의 경우 가끔 프로젝트 경험을 쓸 때 히스토리별로 (과거->현재)로 작성하곤한다. 하지만 앞장이 메인이다. 가장 기술적으로 현재와 가깝고 가장 성장해 있는 최근 경험을 앞장에 쓰자 (현재->과거). 또한, 자신있고 어필하고 싶은 기술 스택을 위주로 앞장에 보여주자. 

 

4. 코딩테스트

코딩테스트 준비

사실 코딩테스트 준비 어떻게 했냐는 질문도 정말 많이 받았던 것 같다.

나는 코딩테스트는 수능 입시 수학이랑 비슷하다는 생각을 종종한다. 우리가 수능 수학문제 어떻게 준비했는가, 먼저 개념 익히고, 그 개념에 따른 유형별 문제 풀어보고, 고난도 문제도 풀어보고 계속해서 반복학습을 한다. 1번부터 28번까지 문제는 유형을 알면 풀 수 있는 문제이기 때문에 그때그때 맞는 유형의 풀이를 기계적으로 하기 위해서 계속해서 반복하고 알던 내용도 계속해서 응용해서 풀어보는 노력을 한다. 그리고 29번 30번의 고난도 문제의 경우에는 앞에서의 유형별 반복학습과 개념의 이해가 먼저 되고나서 고난도 문제 기출을 풀어보곤 한다. 코딩테스트도 똑같다.

알고리즘에도 그리디, 수학, DP, 그래프, 자료구조 등 다양한 유형이 있고 기본적인 1번부터 28번까지의 문제를 풀기 위해 그 문제 유형을 반복적으로 학습하고 풀어본다. 그리고 기업 코딩테스트의 가장 어려운 문제는 29번 30번 처럼 계속해서 해당 기업 코딩테스트 기출의 동향을 보고 어려운 문제를 마주했을 때 어떤 방식으로 접근해야하는지 역시 반복적으로 학습한다. (이 29-30번을 못풀것 같아서 공채 코테가 자신이 없었다)

내가 알고있는 코딩테스트 준비 및 강의 사이트는 아래와 같다

사실 결론은 간단하다 꾸준한 반복학습이다. 나같은 경우엔

  1. 학교 알고리즘 수업으로 기본 개념을 익히고
  2. 코드플러스의 백준 강의를 이용해서 유형별 풀이법을 정리하고
  3. 리트코드, 프로그래머스, 백준 뭐든 이용해서 꾸준히 풀었다.

특히 당시에 리트코드를 애용했는데, 리트코드에서 하루에 한문제씩 알고리즘 문제를 푸는 챌린지가 있었어서 습관만들기 좋았다.

애증의 알고리즘 레포.. 그치만 많이 풀지는 못한 것 같다.

코딩테스트 전과 후

코딩테스트를 보기 전에는 꼭 해당 플랫폼의 사용법에 익숙해지자. 카카오는 프로그래머스, 네이버는 codiliity와 프로그래머스에서 봤던 기억이있는데, 어느 플랫폼이든 코딩테스트 전에 해당 플랫폼 테스트 링크를 주기 때문에 꼭꼭 익히자. 

백준에 익숙해져 있는 학생은 bufferedReader 부터 코딩을 시작할텐데, 프로그래머스에서는 IO 관련 코드를 짤 필요가 없기 때문에, 플랫폼 테스트 없이 그냥 바로 들어가서 시험을 본다면 백프로 당황할 것 같다.

코딩테스트를 볼때 나는 주로 아이패드를 이용해서 문제를 풀곤하는데 그 패드에는 항상 풀이 뿐만 아니라 문제에 대한 간단한 키워드도 함께 정리해두었다. 며칠이 지나고 복기를 위해서 혹은 해당 기업에 또 코딩테스트를 볼 일이 있을때 한번 더 풀어보고 혹이라도 같은 회사니까 비슷한 유형으로 나올까 싶어서 였다. 코딩 테스트 후에도 본인이 푼 문제를 복습하는건 중요하다.

 

5. 면접

면접 시작 전 후

요즘 코로나로 인해 2020년부터 기업에서 온라인 면접을 하고있다. 나도 그래서 실제로 대면으로 면접을 보기보다는 온라인으로 면접을 본게 훨씬 많았는데, 온라인 면접이 처음인 분들을 위한 팁을 아래 적는다.

  • 흰 배경 : 이건 사바사지만 배경보단 나한테만 집중해줬으면 좋겠어서 흰 벽만 보이는 위치에서 면접을 봤다ㅋㅋ
  • 얼굴이 잘 보이게 + 조명 확인 : 비대면이라 만나지 못해서 화면상으로라도 내 인상이 좋아보이게 하려고 했다.
  • 통신 상태 확인
  • 혹시 모르니 노트와 펜 준비하기
딱 이 비율로 화면에 내 얼굴을 위치시켰다ㅋㅋㅋ

4번이 가장 중요하다. 면접을 보다보면 본인이 한 프로젝트의 서버 컴포넌트를 직접 그려서 설명해주세요. 혹은 이해가 안되는데 도식화해서 설명해주세요라는 요구를 받을 수 있다. 대면의 경우에는 회사측에서 준비할테지만 온라인 면접은 그러지 못하니 꼭 옆에 설명을 위한 종이와 펜을 준비하자. (요즘 채용 메일에 준비사항으로 대부분 공지하는 것으로 알고있다.)

나같은 경우엔 아이패드 + 맥북이라는 점을 이용해서 아이패드를 연결하고 화면공유를 한 뒤에 아이패드에 필기하면서 설명했다. (맥북의 사이드카 기능을 찾아보자)

또한 위에 코딩테스트와 마찬가지로 면접도 끝난 직후에 모든 질문내용을 복기해서 정리해두었다. 그리고 그 정리된 내용은 다음 면접을 준비할 때 혹은 다른 회사 면접을 준비할 때 사용했다.

 

면접 준비

면접은 사실 위에 지원서에 말한 내용과 같다. 최대한 관련 내용을 깊게 공부하는게 중요하다. 내가 한 면접 준비 방식은 아래와 같다.

1. 면접 예상 질문 리스트를 찾는다.

처음엔 막막해서 질문 리스트에 대한 구글링을 했다 키워드는 아래와 같다
[자바, 운영체제, 네트워크, 알고리즘, 데이터베이스, 디자인패턴] 면접 질문

요즘 github로 신입개발자 면접 질문 정리 이런식으로 정리해둔게 많아서 좋았다ㅋㅋ 그리고 추가로 내가 했던 프로젝트의 기술 스택 관련해서 면접 질문으로 나올 만한 것들을 선별했다 (docker, k8s, ELK 등)

위 사진은 실제 내가 적어두었던 면접 질문 리스트인데, 훨씬 더 많다. 관련 예상 질문을 받을때 기억해야하는 내용과 대답할 내용의 키워드를 적었다. 외에도 이전 면접을 보면서 받았던 질문들에 대한 리스트도 있었다.

 

2. 답변 키워드를 적어가면서 최대한 깊게 공부한다

답변 키워드를 적은 것은 위 사진에 예시를 적었다. 위 질문 키워드를 바탕으로 실제 면접에서 대답할 내용을 최대한 깊게 공부하자. 깊게 공부하지 않고 정말 간단한 내용만 공부하면 그만큼 말도 간단하게만 할 수 밖에 없다.

예를 들면 면접에서 Java HashMap이란? 이라는 면접 질문이 들어왔다고 하자. 그때 답변은 아래 3가지 컨셉을 바탕으로 내가 알고있는 것들을 최대한 깊게 대답 할 것이다 (그리고 이렇게 대답하기 위해 공부를 했다.)

Q. Java HashMap이 뭔지 설명해주세요

A. 키와 값으로 구성된 구조를 가지는 컬렉션 클래스로, 키에 따라서 값을 조회 삭제 수정 삽입이 가능합니다.

B. Map 인터페이스를 상속받은 구현체로 구현체의 특징이 hashing을 사용한다는 특징이 있습니다. 다른 Map 인터페이스의 구현체로는 treeMap이 있는데 이진 검색트리 형태로 데이터를 저장한다는 특징이 있습니다.

C. 해시테이블은 해싱을 사용한다고 했는데요. 해싱을 사용한다는 것은 객체가 가지고 있는 hashcode값에 따라서 key 테이블이 위치할 곳을 정한다는 것입니다. hashcode 계산으로 해당 데이터가 위치한 곳을 빠르게 찾을 수 있기 때문에 많은 양의 데이터를 검색할 때 유리합니다. 하지만 주의도 필요합니다. 이때 만약 다른 key값인데 hascode 값이 동일한 경우가 있을 수 있는데 이때는 해시 충돌이 일어날 수 있습니다. 이때는 hashcode를 담고있는 bucket이 연결리스트와 같은 구조로 다음 연결 리스트의 노드에 해당하는 key 값을 넣어주어 값을 저장합니다.

실제로 A만 말하더라도 B라는 꼬리 질문이 들어올 수 있고, B까지 말하더라도 C라는 꼬리질문이 들어올 수 있다. 꼬리질문은 그 지식에 대해 깊게 알고있는지를 판단하기 위해 들어오는 것이다. 꼬리질문을 예상해서 답변키워드를 적어가면서 깊게 공부했었다. 본인이 알고있는 지식이라고 생각해도 다시 한번 더 점검해보고 모르는 내용이 없는지 점검하고 더 공부하자.

그리고 다시한번 말하지만 면접에서 내가 모르는 내용을 질문하면 정말로 땡큐다. 모를때는 그냥 A, B까지는 아는데 질문 주신 부분은 잘 모르겠습니다. 공부해야 할 거리가 생겼네요 감사합니다. 하고 넘겼었다. 그렇게 대답하니 실제로 그 다음 면접에서 실제로 몰랐던 내용에 대한 재 답변을 요청했고, 그때는 완벽하게 대답해서 좋은 인상을 남겼다.

 

3. 말로 각 질문에 대한 답변을 소리내서 해본다

공부한 내용을 글로 정리하는 것과 말로 상대방에게 내가 알고있는 것을 전달하는 건 진짜 매우 다르다!!! 이거는 진짜 무조껀 해보고 가야한다. 같이 취업 준비를 하는 친구들과 모의면접을 하고 피드백을 받는 것도 좋다. 

나같은 경우엔 위에 예상 질문 중에 나온다면 이 예시를 들어서 설명해야지 까지 미리 말로 연습해서 갔었다. 예를 들면 템플릿 메서드 패턴의 예시를 들때는 Message라는 추상클래스를 두고 KakaoTalk, InstargramDM, FaceMessanger 클래스를 바탕으로 설명해야지. 동시성 관련한 질문이 들어오면 account 계좌 입출급을 동시에 했을 때에 대한 경우를 예를 들어야지.까지 설정한 후에 말로 해보면서 준비했다.

 

4. 면접에서 공부할 거리

지원서를 쓸 때 받을 수 있는 면접 질문을 생각하면서 썼었다. 따라서 지원서에 쓴 기술 스택을 2번에서 말한 것처럼 제대로 공부하자.

컴퓨터공학 기초적인 내용은 정말 기본이기 때문에 내 경력이 아무리 화려해도 면접에서 언제든 나올 수 있다. 꼭 대비하자. 구글링 해보면 면접에서 물어보는 대표적인 질문들이 몇가지가 있다. 적어도 그정도라도 공부하자 :)

주언어(Java)와 관련한 내용 공부를 해도해도 끝이 없다. 자바 기준으로 컴파일타임 런타임타임에 대한 질문, 내부 JVM 구조, GC, 메모리 저장, 람다, 함수형 프로그래밍, 스트림 등등 언어안에도 너무 많은 내용이 있다. 성능뿐만 아니라, JVM같은 경우엔 운영체제와도 연관되는 내용이며 자바 thread 역시 현업에서의 동시성 프로그래밍을 위한 기초이다. 언어를 공부하다 보면 컴퓨터 공학 지식과 연관되는 부분도 많이 나와서 나는 자바 공부를 진짜 열심히 했었다. (재밌기도했다..) 그러다보니 자바 관련 질문에서는 대부분 답변을 할 수 있었다.

 

6.  결론

나는 결국 위 과정을 통해 약 한 달 간의 취업준비를 했었고 최종적으로 카카오에 가게 되었다. 

  • 네이버 클라우드 채용형 인턴 : 서류 합 > 코테 합 > 1차 면접 합 > 최종 합격 > 거절
  • SAP 인턴 : 서류 합 > 1차 면접 합 > 최종 합격 > 거절
  • 하이퍼커넥트 경력직 > 서류 합  > 코테 불합
  • 카카오 수시채용 : 서류 합 > 코테 합 > 전화 면접 합 > 1차 면접 합 > 2차 면접 합 > 최종 합격 > 취뽀
지금봐도 너무!!!! 많았던 카카오 입사 절차

내가 서류를 넣은 4곳 중 가장 가고싶었던 곳은 카카오였다.  하지만 최종 합격 발표가 난 나머지 두곳이 시기는 더 빨랐었다. 수시 채용이나 인턴을 이용해서 취업을 준비하다보니 그사이 지원 간격이나 계약서 사인을 조금만 기다려달라는 메일도 보내 조정을 해보려했으나, 두곳의 합격 발표가 난 시점이 각각 카카오 전화 면접 합, 2차 면접 결과를 기다리던 시점이었다. 채용 일정 차이로 다른 곳을 포기하거나 카카오 결과를 기다리거나 택일의 상황이었다. 하지만 가장 가고싶었던 곳이 여기었기에, 두 곳 모두 계약서 작성을 거절하고 위와 같은 방식으로 카카오 면접에만 집중했었고 그 결과 카카오 검증형 계약직으로 입사하게 되었다.

그리고 3개월의 계약직 기간을 거쳐, 내부 과제를 마치고 전환 평가에 합격했다.

사실 내 취준 방법이 정답은 아니다. 나처럼 수시채용을 노리는 것보다 공채를 노리는게 훨씬 마음이 편할 수 있다. 또한 각자의 스펙과 전략이 있을거고 나와는 다를 수 있다. 하지만 내가 취준을 하면서 공부한 방식이나 얻었던 정보들이 누군가에게 도움이 될 수 있지 않을까 해서 취준기를 계속 써야지 써야지 하고 계속 미뤘는데, 드디어 글을 마치게 되었다 :) 모두 본인의 강점을 내세울수 있는 취준을 하고 취뽀를 할 수 있기를🙏

Oh man... I can't believe I'm only writing this now.. I kept going back and forth and finally decided to finish this post. (Still, I went to Kakao, but my Naver review is the most viewed post on my blog?? LOL)
 After failing the Naver full-time conversion interview in mid-June, I started my job search.

jyami.tistory.com/116

 

Naver Backend Internship Review

I'm going to write about my Naver internship experience from March to June, about 3 months. I kept putting it off saying I'd write it eventually, and it's been almost 2 months since the internship ended, so quite a bit of time has passed, but anyway

jyami.tistory.com

When I was preparing for the Naver internship, I got in without any algorithm prep or interview preparation at all. Since my very first company interview was that Naver internship interview, I was running on pure hopium thinking if I got into Naver, I could land a job without any real job hunting LOL. But reality hit hard — I unfortunately didn't get converted to full-time ㅠ and I was like "wait, am I actually job hunting now..?" and that's how my job search began LOL

Looking at just the conclusion, I don't think I spent that long stressing about job hunting, but I want to write about my relatively short 1-month job search journey.

 

1. Companies I Wanted to Join

Setting Your Target Companies

I believe the most important thing for any college student starting their job search is to set criteria for the companies they want to join. There are so many different types of companies — public enterprises, startups, large corporations, IT companies, foreign companies, etc. — and each one has different standards for hiring new employees. Also, starting from the ideal candidate profile that your desired company is looking for, to the basic qualifications needed (language certifications, professional certifications, etc.), the hiring process is different for every company, so you need to prepare in advance. 

For example, if I wanted to join Samsung, I would first practice the past coding test problems from Samsung, study for the aptitude test included in their hiring process, and if I made it to interviews, I'd prepare according to the interview order and characteristics of each interview round. These hiring processes are usually described in detail on each company's recruitment guide and process pages. (Make Googling a habit) By looking at these hiring processes, you can objectively assess your current qualifications and prepare for any gaps.

Once you've decided on the company you want to join, you'll be going through a long hiring process and preparing accordingly. After going through all that grueling process, it should be a company you truly want to join — that way, when the acceptance announcement comes, the joy will be that much greater. So I believe it's important for everyone to first choose the company they truly want to work at and focus their preparation on that choice.

I want to work at an IT company..

So I had the keyword "a place where I can grow as a developer" in mind, and I was thinking about IT companies or foreign IT companies. At the time, I submitted applications to these four places: Kakao, Hyperconnect, Naver, and SAP. (There were other IT companies I was interested in, but these four had the job postings that caught my attention at the time)

The ideal candidate profiles required by these four companies, which all shared the common trait of being IT companies, were actually pretty similar. The core keyword was "a developer who loves and excels at development." Looking more closely, things like understanding Java well, having solid computer science knowledge, etc. — the desired technical competencies were detailed in the job descriptions.

From the company's perspective of hiring developers, it felt like they were asking: You'll need to study a lot more going forward, and you need to have read development books, have collaborative development experience, deep understanding of your major's knowledge, and understanding of your primary programming language to work with us — are you ready? That's right. They were asking whether you, as an undergrad with some interest in development, had put in effort and study to improve your development skills, and whether that studying was done properly.

And I felt that my current state could sufficiently demonstrate the required competencies through the studies, projects, and technical interests I'd pursued during my undergraduate years.  Even if there were parts of the job description that didn't match me, I planned to emphasize that I had studied my current tech stack in sufficient depth.

Here's a tip — if you don't know what to study next, look at the qualification requirements of the companies you want to join. In my case, I didn't have Kotlin server development experience, but if I had seen this posting in my third year, I would have found a new thing to study: Kotlin. And even if you already know the content, you should absolutely study it as deeply as you possibly can. For example, if it says "Java-based server development experience," as you can see from some of my blog posts, beyond the Java classes at school, I read books like Effective Java, and when there were concepts I didn't understand like generics, I dug deeper and kept growing. 

 

2. How IT Companies Recruit

The companies I wanted to join were mainly IT companies, and among them, I especially wanted to go to Kakao and Naver. So I looked into and explored many ways to enter these companies as a new hire. From what I could see, these two companies had roughly 3 types of application methods.

  • Open Recruitment (Regular Hiring)
  • Rolling Recruitment
  • Internship (Regular Hiring / Rolling Recruitment)

Open Recruitment

I think most of you are already familiar with open recruitment. Open recruitment is usually divided into first-half and second-half cycles, where they hire a large number of new developers at once. Since open recruitment is a method used by many companies beyond just Kakao and Naver, I think most people already know about it.

However, considering these are IT companies, here are some characteristics of open recruitment:

  • A really large number of applicants
  • The coding test is difficult
  • Interviews mainly focus on basic computer science knowledge questions
  • Interviews can be N:N (applicants : interviewers)
  • You join directly as a full-time employee
  • You have to adjust your job search schedule to match the company's application period
  • If you get in, you'll have peers who joined at the same time
  • After passing, there's a workshop period before you start
  • You don't know which team you'll be assigned to

I mentioned above that the coding test is difficult — among college students preparing for open recruitment, I've seen people who practice past exam problems from these companies for years. In fact, Kakao's open recruitment coding test is conducted on Programmers, and I think I even saw Programmers selling a lecture course analyzing the open recruitment coding test problems (was it 2018?)

Also, this is something I personally noticed while looking at open recruitment postings — they don't list specific technical skill requirements. I got the feeling that regardless of whether you use Java, C, or Python, if you're confident in your development area and have strong computer science abilities, they'll hire you as a new developer.

I didn't apply through open recruitment. Personally, I wasn't confident in coding tests. I did prepare for coding tests in my own way, but I had doubts about whether I could be at the top tier among all those open recruitment applicants. (Though I can solve LeetCode easy~medium problems) I also had doubts about whether they would carefully review my resume among so many applicants. And the most important reason was that I started my job search in June, but I would have had to wait until September for open recruitment, and I thought I could apply to and get accepted at companies in the meantime.

However, the stability of becoming a full-time employee right away and having peers who join together were the biggest advantages of open recruitment and something I was envious of.

 

Rolling Recruitment

This is how I applied to Kakao. Some college students might not know about rolling recruitment. Rolling recruitment means applying to developer positions posted on each company's career site. You might see "(Experienced)" written on these postings and think "I'm a new grad, so I can't apply to this, right?" But if you dig through the career site carefully, you'll sometimes find postings that say "experience level doesn't matter" or include explanations about probation periods for new developers.

It's true that these sites are primarily intended for experienced developer postings, but there are occasional postings that also consider new developers who meet the tech specs required by the team. So if you're targeting rolling recruitment, check your desired company's career site regularly.

Here are some characteristics of IT company rolling recruitment from a new developer's perspective:

  • Hiring considers team culture fit
  • Preference for candidates matching the team's required tech specs
  • New hires may have a 3-month probation period depending on the company
  • If there's a probation period, your continued employment is determined by evaluation (3-month contract)
  • Coding tests vary by team
  • They ask computer science knowledge questions in interviews
  • They dig deep into your experience based on what you wrote in your application
  • Interviews are 1:N (applicant : interviewers)
  • The overall hiring schedule can be adjusted to fit your situation

With rolling recruitment, since I was the only applicant, they tended to tailor their questions to my application. They also asked tough computer science questions to gauge whether I was at the level of an open recruitment new hire. The coding test was, in my experience, a bit easier than open recruitment past exams, but this probably varies by team. (But most are a bit easier than open recruitment..) 

Also, unlike open recruitment, rolling recruitment allowed me to adjust the hiring schedule to fit my own schedule. While open recruitment requires you to take interviews and coding tests on fixed dates, with rolling recruitment, the HR team sends you an email asking about your preferred time and date.

However, when a new hire enters through rolling recruitment like this, there can be penalties like a 3-month probation period. That was my situation, and the uncertainty was a bit tough. Even though I went through a longer hiring process than open recruitment, being on a contract with no peers and the possibility of not being at the company after 3 months was a bit psychologically daunting. 

The advantage of rolling recruitment is that you can choose a company and team that fits what you want, and go through a hiring process that's focused on you. I liked this aspect, which is why I chose rolling recruitment.

 

Internship (Regular Hiring / Rolling Recruitment)

IT companies also hire interns. But there's a saying that "internships are golden tickets" because they're just as hard to get. For internships, I've seen both regular hiring and rolling recruitment formats, and the hiring scale and process really varied company by company. 

For example, Kakao internships were conducted similarly to the Kakao new developer open recruitment mentioned above, but Naver intern regular hiring was done on a smaller scale through NBP (Naver's subsidiary) intern recruitment. 

In my case, I applied for the Naver Cloud regular hiring internship during my June job search, and when I applied for an internship in January, it was through rolling recruitment. Since it's an internship, I felt it was a softer version of the open recruitment and rolling recruitment methods mentioned above. (But still, prepare hard for the interviews)

 

3. Writing Your Application

If you've looked at the application method for your desired team or company in section 2 and made your choice, it's time to write your application for the document screening. Document screening usually allows you to submit the company's standard application form along with your personal resume. I mainly tried to pack my activities into my resume, and in the application form, I expanded on the resume content in a more narrative style. Here are some tips I'd like to share for writing applications:

1. A developer resume's first impression should show you're passionate about development.

Since they're hiring developers — especially new developers — I thought my documents should be brimming with passion for development. So when writing my application, I tried to highlight these 5 things:

  • Reading development books: Showing I made efforts to go beyond school knowledge and get closer to being a real-world developer
  • Hackathons: (Well... I just did them for fun, but) Demonstrating a fast learning curve by quickly picking up unfamiliar stacks when needed
  • Projects / Extracurricular activities: Showing I can collaborate with others using technology
  • Computer science knowledge (GPA): Showing I diligently completed the developer fundamentals 
  • Studying my primary language (Java): Showing I can use my language according to its characteristics

These 5 things were what I could use to demonstrate my passion as a developer. As a new grad, what you can highlight in your application isn't real work experience, but rather how you've been studying development, how interested you are in development, and what kind of developer you're growing into — so weave in your experiences and write them in a clear, organized manner.

Actually LOL, so when I applied to Hyperconnect, I boldly submitted my application to an experienced-hire posting despite being a new grad, and after reviewing my documents, they confirmed I was about to graduate and let me interview..ㅇ0ㅇ But I failed the coding test (Hyperconnect's coding test was really interesting — instead of algorithm tests, they mainly asked about real-world code and it was in English)

2. Keep your resume updated as you go

As you participate in various extracurricular activities and clubs, you end up writing multiple applications and your history keeps getting updated. And you'll have takeaways from each activity. Make sure to document those takeaways when they're fresh. 

All the applications on my personal MacBook
  • 1st year - LIKELION: Interest in web, became able to do web development with MVC structure / Focus on completing services regardless of front or back end
  • 2nd year - SOPT: Became able to do web development with API structure / Learned Spring Boot / Became proficient in Java / Interested in backend development
  • 2nd year - Smilegate: Understanding of server architecture / Became proficient in Spring
  • 3rd year - JavaBom: Fundamental knowledge needed as a backend developer / Clean code / Writing test code / Deeper understanding of Spring and Java 
  • 3rd year - DSC: Running a developer community
  • 4th year - Internship and job search preparation

In my case, the rough history was as above, and in between, I also participated in many activities like Open Hack, Naver Hack Day, etc. Through those activities, I wrote applications, and as I kept writing them, I'd just tweak them a little and keep reusing them. Going all the way back, the application I wrote for SOPT became the first draft of the self-introduction letter I used later. So make sure to save everything well in your own way.

3. When writing your application, think about potential interview questions, and thoroughly study the tech stacks you mention

The tech stacks you write in your application are basically expected interview questions. So study everything you put in your application deeply. Let's say you wrote in your self-introduction that you have experience using MySQL. Then the interviewer could ask so many questions — from MySQL characteristics to DB normalization, transactions, indexing, isolation levels, and more. And even outside of interviews, having a deep understanding of the tech stacks you've studied is fundamental knowledge every developer should have. So questions about those technologies in interviews are really hinting at things you should basically know. Whenever I got a question about something I didn't know, I thought "thank you" and immediately studied it in depth. (In fact, in the next interview, they asked me to re-answer questions I couldn't answer before.)

Also, if I felt I couldn't answer well in an interview, or if I had only surface-level knowledge about something, I boldly removed it from my application. In my case, I had freelance frontend work experience, but I removed that from my application because I was worried they might ask about frontend stuff. If they asked me to explain JavaScript event handling, I wouldn't have been confident answering that, and I definitely couldn't have handled follow-up questions either. Plus, even with just the tech stacks already in my application, I had mountains of content to study before the interview — when would I study frontend too?

4. Make your resume easy to read + The first page is the main page

Standing out in your application matters too. With so many applicants applying to these companies, if the content is repetitive, the subject-predicate relationships are off, and it's written in a way the reader can't understand — imagine how exhausting that would be to read. No matter how impressive your experiences and insights are, summarize them as much as possible in your application (the interviewers will see that summary and ask about what intrigues them anyway.) 

So I personally used illustration tools to add graphs, color coding, bolding, bullet points — I did everything I could to improve readability. I also asked developer friends I knew to review it.

jyami.tistory.com/8

 

Backend Developer Resume

Developer portfolio, backend developer resume (update 2020.06.12)

jyami.tistory.com

Also, with resumes, the first page is the main page. College students sometimes write their project experiences in chronological order (past→present). But the first page is the main page. Put your most recent experience — the one closest to the present and showing the most growth — on the first page (present→past). Also, showcase the tech stacks you're most confident in and want to highlight on the front page. 

 

4. Coding Tests

Preparing for Coding Tests

I think I got asked a lot about how I prepared for coding tests.

I often think coding tests are similar to the math section of the Korean college entrance exam (수능). How did we prepare for the math section? First learn the concepts, then practice problems by type, try difficult problems too, and keep doing repetitive learning. Problems 1 through 28 can be solved if you know the patterns, so you keep repeating and applying what you know to mechanically solve each pattern type. And for the hard problems like 29 and 30, you first need the pattern-based repetitive learning and conceptual understanding, then practice with past difficult problems. Coding tests are exactly the same.

Algorithms also have various types like greedy, math, DP, graphs, data structures, etc., and you repeatedly study and practice those problem types to solve the basic problems 1 through 28. And for the hardest problems on company coding tests, like problems 29 and 30, you keep studying trends from that company's past coding tests and repeatedly learn how to approach difficult problems. (I wasn't confident in open recruitment coding tests because I couldn't solve these 29-30 level problems)

Here are the coding test preparation and lecture sites I know of:

The conclusion is actually simple: consistent, repetitive practice. In my case:

  1. I learned the basic concepts through my school's algorithm class
  2. I organized problem-solving methods by type using Code Plus's Baekjoon lectures
  3. I consistently solved problems on LeetCode, Programmers, Baekjoon — whatever was available.

I especially loved LeetCode at the time because they had a daily algorithm problem challenge, which was great for building habits.

My love-hate algorithm repo.. but I don't think I solved that many.

Before and After the Coding Test

Before taking the coding test, make sure you're familiar with the platform. Kakao uses Programmers, and I remember Naver using Codility and Programmers. Whatever the platform, they give you a test link beforehand, so definitely practice with it. 

Students who are used to Baekjoon will start coding from bufferedReader, but on Programmers you don't need to write IO-related code. So if you just go in and take the test without trying the platform first, you'll 100% be thrown off.

When taking coding tests, I usually used my iPad to work through problems, and on that iPad I always noted not just the solutions but also brief keywords about each problem. This was for reviewing days later, or in case I had another coding test at the same company, so I could try solving them again and see if similar types might come up since it's the same company. Reviewing the problems you solved after the coding test is important.

 

5. Interviews

Before and After the Interview

Due to COVID, companies have been conducting online interviews since 2020. I also ended up doing way more online interviews than in-person ones. Here are some tips for those doing online interviews for the first time:

  • White background: This varies by person, but I wanted them to focus only on me rather than the background, so I positioned myself in front of a white wall for interviews LOL
  • Make sure your face is clearly visible + check the lighting: Since we can't meet in person, I tried to make a good impression through the screen.
  • Check your internet connection
  • Have a notebook and pen ready just in case
This is exactly the ratio I positioned my face on screen LOL

Number 4 is the most important. During an interview, you might be asked to draw and explain the server components of a project you worked on, or to diagram something for better understanding. For in-person interviews, the company would prepare materials, but for online interviews they can't, so definitely have paper and a pen ready for explanations. (These days, I believe most companies include this in the preparation instructions in their hiring email.)

In my case, I took advantage of having an iPad + MacBook by connecting the iPad, sharing the screen, and writing on the iPad while explaining. (Look up the Mac Sidecar feature)

Also, just like with the coding test above, right after each interview ended, I reviewed and documented all the questions. I then used those notes to prepare for the next interview or interviews at other companies.

 

Interview Preparation

Interview preparation is honestly the same as what I said about the application above. The key is to study related content as deeply as possible. Here's how I prepared for interviews:

1. Find lists of expected interview questions.

I was overwhelmed at first, so I Googled question lists. The keywords were:
[Java, Operating Systems, Networking, Algorithms, Databases, Design Patterns] interview questions

These days there are lots of well-organized resources on GitHub like "new developer interview question compilations," which was great LOL. Additionally, I selected potential interview questions related to the tech stacks from my projects (Docker, K8s, ELK, etc.)

The photo above shows the actual interview question list I wrote down, and there's much more. I wrote down keywords for what to remember and how to answer each expected question. There was also a list of questions I'd received from previous interviews.

 

2. Write down answer keywords and study as deeply as possible

I showed an example of writing down answer keywords in the photo above. Based on those question keywords, study as deeply as possible for the actual interview answers. If you only study simple content without going deep, you'll only be able to give simple answers.

For example, let's say the interview question "What is Java HashMap?" comes up. I would answer as deeply as possible based on these 3 concepts (and I studied specifically to be able to answer this way):

Q. Please explain what Java HashMap is.

A. It's a collection class with a key-value structure that allows you to look up, delete, modify, and insert values by key.

B. It's an implementation of the Map interface, and the key characteristic of this implementation is that it uses hashing. Another implementation of the Map interface is TreeMap, which stores data in a binary search tree format.

C. I mentioned that hash tables use hashing. Using hashing means that the location where a key will be placed in the table is determined by the object's hashcode value. Since you can quickly find where data is located through hashcode calculation, it's advantageous when searching through large amounts of data. But caution is needed. If different keys happen to have the same hashcode, a hash collision can occur. In that case, the bucket containing the hashcode uses a structure like a linked list, placing the key value in the next node of the linked list to store the value.

In reality, even if you only say A, a follow-up question B could come. And even if you say up to B, a follow-up question C could come. Follow-up questions are asked to determine if you have deep knowledge of the subject. I studied deeply by anticipating follow-up questions and writing down answer keywords. Even if you think you already know something, double-check it, look for gaps in your knowledge, and study more.

And once again — if they ask me something I don't know in an interview, I'm genuinely thankful. When I didn't know, I'd say "I know up to A and B, but I'm not sure about what you're asking. Looks like I've found something new to study — thank you." and move on. When I answered that way, in the next interview they actually asked me to re-answer something I didn't know before, and that time I answered perfectly and left a good impression.

 

3. Practice answering each question out loud

Organizing what you've studied in writing versus verbally conveying what you know to someone else are truly VERY different!!! You absolutely must try this before going in. It's also great to do mock interviews with friends who are also job hunting and get feedback. 

In my case, for the expected questions above, I even practiced to the point of deciding which examples to use. For example, when explaining the Template Method pattern, I'd use an abstract class called Message with KakaoTalk, InstagramDM, and FaceMessenger classes. When a concurrency question came up, I'd use the example of depositing and withdrawing from an account simultaneously. I prepared by setting up these scenarios and practicing out loud.

 

4. What to study for interviews

When writing the application, I thought about potential interview questions. So study the tech stacks in your application thoroughly, as mentioned in point 2.

Basic computer science knowledge is truly fundamental, so no matter how impressive your background is, it can come up anytime in an interview. Definitely prepare for it. If you Google it, there are several representative questions that commonly appear in interviews. Study at least those :)

Studying content related to your primary language (Java) never ends. For Java alone, there are questions about compile time vs runtime, internal JVM structure, GC, memory storage, lambdas, functional programming, streams, and so much more. Beyond just performance, the JVM is also related to operating systems, and Java threads are the foundation for concurrency programming in the real world. As I studied the language, a lot of it connected to computer science knowledge, so I really studied Java hard. (It was fun too..) As a result, I was able to answer most Java-related questions.

 

6.  Conclusion

In the end, I went through the process described above over about a month of job hunting, and ultimately ended up joining Kakao. 

  • Naver Cloud Recruitment Intern: Resume passed > Coding test passed > 1st interview passed > Final offer > Declined
  • SAP Intern: Resume passed > 1st interview passed > Final offer > Declined
  • Hyperconnect Experienced Hire > Resume passed  > Coding test failed
  • Kakao Rolling Recruitment: Resume passed > Coding test passed > Phone interview passed > 1st interview passed > 2nd interview passed > Final offer > Got the job!
Even looking at it now, there were SO many steps in Kakao's hiring process!!!!

Out of the 4 places I applied to, Kakao was where I wanted to go the most.  However, the other two places that gave me final offers did so earlier in the timeline. Since I was preparing through rolling recruitment and internships, I tried to adjust the timing by sending emails asking them to wait a bit before signing the contract, but the two offers came in while I was waiting for Kakao's phone interview results and 2nd interview results, respectively. Due to the difference in hiring schedules, I was in a situation where I had to choose between giving up the other places or waiting for Kakao's results. But since Kakao was where I wanted to go the most, I declined to sign the contracts at both places and focused solely on the Kakao interviews using the methods described above — and as a result, I joined Kakao as a probationary contract employee.

After a 3-month contract period, I completed the internal assignment and passed the conversion evaluation.

Honestly, my job hunting approach isn't the definitive answer. Unlike me who targeted rolling recruitment, aiming for regular mass recruitment cycles might be much less stressful. Everyone has their own qualifications and strategies, and they might be different from mine. But I thought the study methods and information I gathered during my job search might help someone out there, and I kept putting off writing this job hunting story, saying "I should write it, I should write it" — but I've finally finished it :) I hope everyone can leverage their own strengths in their job search and land their dream job 🙏

댓글

Comments

Dev Book Review/Java8 in Action

[자바8인액션] Chap17. 리액티브 프로그래밍

소스코드https://github.com/mjung1798/Jyami-Java-Lab/tree/master/java8-in-actionmjung1798/Jyami-Java-Lab💻 Jyami의 Spring boot 및 Java 실험소 💻. Contribute to mjung1798/Jyami-Java-Lab development by creating an account on GitHub.github.com리액티브 프로그래밍에서는 다양한 시스템과 소스에서 들어오는 데이터 항목 스트림을 비동기적으로 처리하고 합쳐서 문제를 해결한다 - 빅데이터 : 빅데이터는 페타 바이트 단위로 구성되며 매일 증가한다 - 다양한 환경 : 모바일 디바이스 부터 수천개의 멀티 코어 프로세서로 실행되는 클라우드 기반 클러스터까지 다..

[자바8인액션] Chap17. 리액티브 프로그래밍

728x90

소스코드

https://github.com/mjung1798/Jyami-Java-Lab/tree/master/java8-in-action

mjung1798/Jyami-Java-Lab

💻 Jyami의 Spring boot 및 Java 실험소 💻. Contribute to mjung1798/Jyami-Java-Lab development by creating an account on GitHub.

github.com

리액티브 프로그래밍에서는 다양한 시스템과 소스에서 들어오는 데이터 항목 스트림을 비동기적으로 처리하고 합쳐서 문제를 해결한다
- 빅데이터 : 빅데이터는 페타 바이트 단위로 구성되며 매일 증가한다
- 다양한 환경 : 모바일 디바이스 부터 수천개의 멀티 코어 프로세서로 실행되는 클라우드 기반 클러스터까지 다양한 환경에 배포된다.
- 사용패턴 : 사용자는 1년 내내 항상 서비스를 이용할 수 있으며 밀리초 단위의 응답 시간을 기대한다.

리액티브 프로그래밍 패러다임에 맞게 설계된 애플리케이션은 높은 응답성을 제공한다.
전체의 리액티브 시스템을 구성하는 여러 컴포넌트를 조절에서도 가용성을 제공한다.

1. 리액티브 매니페스토

www.reactivemanifesto.org/

The Reactive Manifesto

Responsive: The system responds in a timely manner if at all possible. Responsiveness is the cornerstone of usability and utility, but more than that, responsiveness means that problems may be detected quickly and dealt with effectively. Responsive systems

www.reactivemanifesto.org

리액티브 매니페스토에서는 리액티브 애플리케이션과 시스템 개발의 핵심 원칙을 공식적으로 정의한다. 

 

 

  • 반응성(responsive) : 시스템은 가능한한 적정시간 안에 반응한다. 반응성이 뒷받침 되어야 사용성을 높일 수 있다.
  • 탄력성(elastic) : 다양한 작업 부하에도 시스템 반응성이 유지된다. 입력속도가 바뀐다면 이들 입력 관련 서비스에 할당된 자원을 늘리거나 줄임으로 반응할 수 있다.
  • 메시지 주도(message driven) : 컴포넌트 간의 약산 결합, 고립, 위치, 투명성이 유지되도록 시스템은 비동기 메시지 전달에 의존한다.
  • 회복성(resilient) : 장애 시에도 시스템의 반응성은 유지된다.

1. 애플리케이션 수준의 리액티브

애플리케이션 수준에서의 주요기능은 비동기로 작업을 수행할 수 있다는 점이다.
비동기로 처리하는 것이 최신 멀티코어 CPU 사용률을 극대화(내부적으로 경쟁하는 CPU의 스레드 사용률) 할 수 있는 방법이다

이를 위해 스레드를 퓨처, 액터, 일련의 콜백을 발생시키는 이벤트 루프 등과 공유하고 처리할 이벤트를 변환하고 관리한다.

개발자 입장에서는 동기 블록, 경쟁 조건, 데드락 같은 저 수준의 멀티스레드 문제를 직접 처리할 필요가 없어져 비즈니스 요구사항 구현에 더 집중이 가능하다.

RxJava, Akka같은 리액티브 프레임워크는 별도로 지정된 스레드 풀에서 블록 동작을 실행시킨다. > 메인 풀의 모든 스레드는 방해 받지 않고 실행된다. (CPU 관련 작업과 IO관련 작업의 분리)

2. 시스템 수준의 리액티브

리액티브 시스템  = 소프트웨어 아키텍처 : 여러 애플리케이션이 한 개의 일관적인, 회복할 수 있는 플랫폼을 구성할 수 이쎅 해줄 뿐 아니라 이들 애플리케이션 중 하나가 실패해도 전체 시스템이 계속 운영되게 해준다.

- 리액티브 애플리케이션 : 이벤트 주도
- 리액티브 시스템 : 메시지 주도

컴포넌트에서 발생한 장애를 고립시킴으로 문제가 주변의 다른 컴포넌트로 전파되면서 전체 시스템 장애로 이어지는 것을 막음으로 회복성을 제공한다(== 결함 허용 능력) : 고립과 비결합

모든 컴포넌트가 수신자의 위치에 상관없이 모든 서비스와 통신 ( 탄력성 = 위치 투명성)

 

2. 리액티브 스트림과 플로 API

리액티브 프로그래밍 = 리액티브 스트림을 사용하는 프로그래밍
리액티브 스트림 : 잠재적으로 무한의 비동기 데이터를 순서대로 처리하고, 블록하지 않는 역압력을 전제해 처리하는 표준 기술

역압력 : pub-sub 프로토콜에서 publisher가 발행하는 속도 > subscriber가 소비하는 속도 일 경우에 문제가 발생하지 않도록 보장하는 장치 + 기존 데이터 처리에 얼마나 시간이 걸리는지 업스트림 발행자에게 알릴 수 있어야함

데이터 수신자가 스레드를 블록하지 않고도 데이터 수신자가 처리할 수 없을 만큼의 데이터를 받는 일을 방지한다.

리액티브 스트림이 구현이 제공해야 하는 최소 기능 집합 (네개 인터페이스)

  • java9의 새로운 java.util.concurrent.Flow 클래스
  • Akka 스트림, 리액터(reactor), RxJava, Vert.x 등 많은 서드 파티 라이브러리

 

2-1. Flow 클래스

Publisher가 항목을 발행하면 Subscriber가 한 개씩 또는 한 번에 여러 항목을 소비하는데 Subscription이 이 과정을 관리하도록 여러 정적 메서드를 제공한다.

public final class Flow {
    static final int DEFAULT_BUFFER_SIZE = 256;

    private Flow() {
    }

    public static int defaultBufferSize() {
        return 256;
    }

    public interface Processor<T, R> extends Flow.Subscriber<T>, Flow.Publisher<R> {
    }

    public interface Subscription {
        void request(long var1);

        void cancel();
    }

    public interface Subscriber<T> {
        void onSubscribe(Flow.Subscription var1);

        void onNext(T var1);

        void onError(Throwable var1);

        void onComplete();
    }

    @FunctionalInterface
    public interface Publisher<T> {
        void subscribe(Flow.Subscriber<? super T> var1);
    }
}
  • Publisher : 많은 이벤트 제공이 가능하지만 Subscriber의 요구사하에 따라 역압력 기법에 의해 이벤트 제공 속도가 제한
  • Subscriber : Publisher가 발행한 이벤트의 리스너로 자신을 등록할 수 있음.
onSubscribe onNext* (onError| onComplete)?

- onSubscribe 메서드는 항상 처음 호출
- onNext 메서드는 여러번 호출 가능
- 이벤트 스트림이 영원히 지속되거나 onComplete, onError 호출 가능

 

 

규칙

  • Publisher는 Subscription의 request 메서드에 정의된 개수 이하의 요소만 Subscriber에 전달한다.
  • Publisher는 동작이 성공적으로 끝나면 onComplete, 문제가 발생하면 onError를 호출해 Subscription을 종료한다.
  • Subscriber는 요소를 받아 처리할 수 있음을 Publisher에게 알려야 한다. (역압력 행사)
  • onComplete나 onError를 처리하는 상황에서 Subscriber는 Publisher나 Subscription의 어떤 메서드도 호출 할수도없고 Subscription이 취소되었다 가정한다.
  • Subscriber는 Subscription.reqeust() 호출 없이도 언제나 종료 시그널을 받을 준비가 되어있어야한다.
  • Subscriber는 Subscription.canel() 이 호출된 이후에도 한 개 이상의 onNext를 받을 준비가 되어있어야 한다.

이 명세에 맞춰 직접 구현한 기능은 Reative Streams TCK라는 툴로 검증할 수 있다. 구현이 까다로움..

Processor 인터페이스 ; Publisher, Subscriber 상속
리액티브 스트림에서 처리하는 이벤트의 변환 관계를 나타낸다.(?)

Flow 클래스의 인터페이스는 직접 구현하도록 의도된게 아니다. (실제로 자바9에서 구현 클래스를 제공하지않는다.)
라이브러리가 준수해야할 규칙과 리액티브 애플리케이션이 서로 협동 소통할 수 있는 공용어 제시가 목표였기 때문이다.
Akka, RxJava 등의 리액티브 라이브러리에서 이미 구현체부터 만들어둔 상태였기 때문에.

2-2. 간단한 리액티브 애플리케이션

@Getter
public class TempInfo {
    public static final Random random = new Random();

    private final String town;
    private final int temp;

    public TempInfo(String town, int temp) {
        this.town = town;
        this.temp = temp;
    }

    public static TempInfo fetch(String town){
        if(random.nextInt(10) == 0)
            throw new RuntimeException("Error!");
        return new TempInfo(town, random.nextInt(100));
    }

    @Override
    public String toString() {
        return "TempInfo{" +
                "town='" + town + '\'' +
                ", temp=" + temp +
                '}';
    }
}
public class TempSubscriber implements Flow.Subscriber<TempInfo> {

    private Flow.Subscription subscription;

    @Override
    public void onSubscribe(Flow.Subscription subscription) {
        this.subscription = subscription;
        subscription.request(1); // 구독 저장하고 첫번째 요청 전달 (1개의 요청을 받을 준비가 되어있다.)
    }

    @Override
    public void onNext(TempInfo tempInfo) {
        System.out.println(tempInfo); // 수신한 온도 출력하고 다음 정보 요청
        subscription.request(1);
    }

    @Override
    public void onError(Throwable throwable) {
        System.err.println(throwable.getMessage()); // 에러 메세지 출력
    }

    @Override
    public void onComplete() {
        System.out.println("Done!");
    }
}
public class TempSubscription implements Flow.Subscription {

    private final Flow.Subscriber<? super TempInfo> subscriber;
    private final String town;

    public TempSubscription(Flow.Subscriber<? super TempInfo> subscriber, String town) {
        this.subscriber = subscriber;
        this.town = town;
    }

    @Override
    public void request(long n) {
        for (long i = 0L; i < n; i++) {
            try {
                subscriber.onNext(TempInfo.fetch(town)); // 현재 온도를 Subscriber로 전달
            }catch (Exception e){
                subscriber.onError(e); // 온도 가져오기를 실패하면 Subscriber로 에러 전달
                break;
            }
        }
    }

    @Override
    public void cancel() {
        subscriber.onComplete(); // 구독이 취소되면 완료 신호를 Subscriber에 전달
    }
}
public class Main {
    public static void main(String[] args){
        getTemperatures("Seoul").subscribe(new TempSubscriber()); // 서울에 Publisher를 만들고 TempSubscriber를 구독
    }

    private static Flow.Publisher<TempInfo> getTemperatures(String town){ // Publisher.subscribe() 메서드 구현
        return subscriber -> subscriber.onSubscribe(new TempSubscription(subscriber, town));
        // 구독한 Subscriber에게 TempSubscription을 전송하는 Publisher를 반환
    }
}
  1. Publisher는 functional 인터페이스이며, 인자로 subscriber를 받는다.
  2. Publisher는 인자로 받은 subscriber의 onSubscribe 메서드를 호출한다.
  3. onSubscribe 메서드 호출로 subscriber는 subscription 객체를 인스턴스 변수로 갖고있게 된다.
  4. 이 subscription 객체는 subscriber를 인스턴스 변수로 갖고있게 된다.
  5. onSubscribe 메서드 호출과 함께, subscription.request(1) 이 불리게 되면서, subscriber가 1개의 요청을 받을 준비가 되었음을 subscription이 알게된다.
  6. subscription.request(1)로 subscription 내의 인스턴스 변수인 subscriber의 onNext 혹은 onError 가 불리게된다.
  7. onNext의 경우에 마찬가지로 subscription.request(1) 이 불리면서 subscriber가 1개의 요청을 받을 준비가 되었음을 subscription이 알게된다.
  8. onError의 경우에는 더이상 subscription에 request 가 일어나지 않아 종료된다.

즉. Publisher의 subscribe로 인해 계속 이벤트가 방출되게 되고, 이 이벤트를 Subscription의 request 요청이 있어야지만 Subscriber 가 이벤트를 소비하게된다.

이때 에러를 발생시키는 코드를 없애게되면 재귀호출로인한 stackoverflow가 발생한다. : Executor를 TempSubscription으로 추가한 다음 다른 스레드에서 TempSubscriber로 전달한다. 

private static final ExecutorService executor = Executors.newSingleThreadExecutor();

    @Override
    public void request(long n) {
        executor.submit(() -> {
            for (long i = 0L; i < n; i++) {
                try {
                    subscriber.onNext(TempInfo.fetch(town)); // 현재 온도를 Subscriber로 전달
                }catch (Exception e){
                    subscriber.onError(e); // 온도 가져오기를 실패하면 Subscriber로 에러 전달
                    break;
                }
            }
        });
    }

3. Processor로 데이터 변환하기

Processor는 Subscriber이며 Publisher이다.

public class TempProcessor implements Flow.Processor<TempInfo, TempInfo> {

    private Flow.Subscriber<? super TempInfo> subscriber;

    @Override
    public void subscribe(Flow.Subscriber subscriber) {
        this.subscriber = subscriber;
    }

    @Override
    public void onNext(TempInfo tempInfo) {
        int fTemp = (((tempInfo.getTemp() - 32) * 5) / 9); // 섭씨로 변환한 다음 TempInfo 다시 전송
        subscriber.onNext(new TempInfo(tempInfo.getTown(), fTemp));
    }

    // 다른 모든 신호는 업스트림 구독자에게 전달.
    @Override
    public void onSubscribe(Flow.Subscription subscription) {
        subscriber.onSubscribe(subscription);
    }

    @Override
    public void onError(Throwable throwable) {
        subscriber.onError(throwable);
    }

    @Override
    public void onComplete() {
        subscriber.onComplete();
    }
}
    @Test
    @DisplayName("processor를 이용한 C=>F 온도 변환")
    void name() {
        getCelsiusTemperatures("Seoul").subscribe(new TempSubscriber());
    }

    public static Flow.Publisher<TempInfo> getCelsiusTemperatures(String town){
        return subscriber -> {
            TempProcessor processor = new TempProcessor();
            processor.subscribe(subscriber);
            processor.onSubscribe(new TempSubscription(processor,town));
        };
    }

Subscriber 인터페이스를 구현하는 다른 모든 메서드는 단순히 수신한 모든 신호를 업스트림 Subscriber로 전달하며 Publisher의 subscribe 메서드는 업스트림 Subscriber를 Processor로 등록하는 동작을 수행한다.

4. 자바에서 플로 API의 구현을 제공하지 않는 이유

API를 만들 당시 Akka, RxJava 등 다양한 리액티브 스트림의 자바 코드 라이브러리가 이미 존재했기 때문이다. 둘다 같은 pub-sub 패턴을 기반한 리액티브 프로그래밍을 구현했지만 이들 라이브러리는 독립적으로 구현이 되었고 각기다른 이름 규칙과 API를 사용함

그래서 플로 API에서 리액티브 개념의 구현을 위한 표준화 작업을 하게된 것이다.

리액티브 스트림의 구현은 대부분 기존 구현을 따라가자.

 

3. 리액티브 라이브러리 RxJava 사용하기

RxJava는 자바로 리액티브 애플리케이션을 구현하는 데 사용하는 라이브러리이다.

github.com/ReactiveX/RxJava

ReactiveX/RxJava

RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM. - ReactiveX/RxJava

github.com

reactivex.io/

ReactiveX

CROSS-PLATFORM Available for idiomatic Java, Scala, C#, C++, Clojure, JavaScript, Python, Groovy, JRuby, and others

reactivex.io

 

RxJava 에서 제공하는 Flow.Publisher 구현 클래스

1. Flowable

public abstract class Flowable<@NonNull T> implements Publisher<T> {}

역압력 기능이 있다. : 너무 빠른 속도로 데이터를 발행해 Subscriber가 이를 감당할 수 없는 상황에 이르는걸 방지하는 기능

2. Observable

public abstract class Observable<@NonNull T> implements ObservableSource<T> {}

역압력 기능을 제공하지 않던 기존버전의 RxJava에서 제공하던 클래스

단순한 프로그램, 마우스 움직임 같은 사용자 인터페이스에 적합. (GUI이벤트나 자주 발생하지 않는 종류의 이벤트에 역압력을 적용하지 말자.)

모든 Publisher는 Subscription의 request(Long.MAX_VALUE); 메서드를 이용해 역압력 기능을 끌 수 있다.

 

3-1. Observable 만들고 사용하기

Observable과 Flowable 클래스는 다양한 종류의 리액티브 스트림을 편리하게 만들도록 여러 팩토리 메서드를 제공한다
이들은 모두 Publisher를 구현하므로 팩토리 메서드는 리액티브 스트림을 만드는 것이다.

    @Test
    @DisplayName("Observable just 메서드 : Observable의 Subscriber는" +
            " onNext(first) onNext(second) onComplete() 순서로 메서드를 받는다.")
    void observableJust() {
        Observable<String> just = Observable.just("first", "second");
    }

reactivex.io/documentation/operators/just.html

    @Test
    @DisplayName("Observable interval 메서드 : 지정된 속도로 이벤트를 방출하는 상황에서 사용한다." +
            "0에서 시작해 1초 간격으로 long 형식의 값을 무한으로 증가시키며 값을 방출한다.")
    void observableInterval() {
        Observable<Long> interval = Observable.interval(1, TimeUnit.SECONDS);
    }

reactivex.io/documentation/operators/interval.html

 

RxJava에서의 Flow.Subscriber 역할

public interface Observer<@NonNull T> {

    void onSubscribe(@NonNull Disposable d);

    void onNext(@NonNull T t);

    void onError(@NonNull Throwable e);

    void onComplete();

}

Subscriber과 같은 메서드를 정의하며, onSubcribe 메서드가 Subscription 대신 Disposale 인수를 갖는다는 점만 다르다.
Observable은 역압력을 지원하지 않아 Subscription의 request가 필요하지 않다.

하지만 onNext가 좀더 유연하다 (더 많은 오버로드 기능 제공) : ex onNext만 구현하고 나머지는 구현하지 않는 기본동작의 Observer 제공가능.

 

 

    @Test
    @DisplayName("RxJava Observer: 많은 오버로드로 인해 유연하게 구현이 가능하다.")
    void observableIntervalWithObserver() {
        Observable<Long> onePerSec = Observable.interval(1, TimeUnit.SECONDS);
        onePerSec.subscribe(i -> System.out.println(TempInfo.fetch("Seoul")));
    }

1초에 한번씩 TempInfo의 temp 값을 출력하리라 예상한다.

그렇지만 Observable이 RxJava의 연산 스레드 풀인 데몬 스레드에서 실행되어, 위 코드에서는 실행하자마자 실행할 코드가 없어 종료되게 된다. 데몬스레드도 함께 종료 > blockingSubscribe를 사용해 결과를 볼 순 있다.

심화예제

    @Test
    @DisplayName("좀더 일반화를 위해 온도를 직접 출력하지 않고 " +
            "사용자에게 팩토리 메서드를 제공해 매초마다 온도를 방출하는 Observable을 만들어보자" +
            "이렇게 할 경우 observer는 그저 받은 데이터만 출력하는 기능을 하면된다." +
            "observable로 온도를 얻는 과정에서 에러와 완료처리 로직을 다 구현했기 때문에 ")
    void observableExample() {
        Observable<TempInfo> seoul = getTemperature("Seoul");
        seoul.blockingSubscribe(new TempObserver());
    }

    public static Observable<TempInfo> getTemperature(String town){
        return Observable.create(emitter ->
                Observable.interval(1, TimeUnit.SECONDS) // 매 초마다 무한으로 증가하는 long 반환 Observable
                        .subscribe(i -> {
                            if(!emitter.isDisposed()){ // 소비된 옵저버가 폐기되지 않았으면 어떤 작업을 수행(에러가 안났으면)
                                if(i >= 5){ // 5번 온도를 onNext 했으면 성공처리 후 종료
                                    emitter.onComplete();
                                }else {
                                    try {
                                        emitter.onNext(TempInfo.fetch(town)); // 온도를 observer로 보
                                    }catch (Exception e){
                                        emitter.onError(e);// 에러가 발생하면 Observer에게 알림
                                    }
                                }
                            }
                        })
        );
    }
    
    public class TempObserver implements Observer<TempInfo>{

        @Override
        public void onSubscribe(@NonNull Disposable d) {
        }

        @Override
        public void onNext(@NonNull TempInfo tempInfo) {
            System.out.println(tempInfo);
        }

        @Override
        public void onError(@NonNull Throwable e) {
            System.err.println("Got Problem " + e.getMessage());
        }

        @Override
        public void onComplete() {
            System.out.println("Done!");
        }
    }

여기서 사용된 emitter 변수는 그 실체가 ObservableEmitter 인터페이스인데, 그 인터페이스는 Emitter의 인터페이스를 상속한다.. : onSubscribe가 빠진 Observer와 같음.

public interface ObservableEmitter<@NonNull T> extends Emitter<T> {}
public interface Emitter<@NonNull T> {

    void onNext(@NonNull T value);

    void onError(@NonNull Throwable error);

    void onComplete();
}

여기서 Observable이 역압력을 지원하지 않아 전달된 요소를 처리한 다음 추가 요소를 요청하는 request()가 필요가 없다.

 

3-2. Observable을 변환하고 합치기

리액티브 라이브러리는 자바 9 플로 API에 비해 스트림을 합치고 만들고 거르는 등의 풍부한 메서드를 제공하는 것이 장점이다. 스트림을 다른 스트림의 입력으로 사용도 가능하며, 스트림의 매핑 함수로 요소를 변환하거나, 다양한 방법으로 합치는 등의 작업도 가능하다.

마블 다이어그램(marble diagram) : 수평선으로 표시된 리액티브 스트림에 임의의 순서로 구성된 요소가 기하학적 모양으로 나타난다.
- 특수기호: 에러나 완료신호를 나타낸다.
- 박스 : 연산이 요소를 어떻게 변화하거나 여러 스트림을 어떻게 합치는지를 보여줌

reactivex.io/documentation/operators

ReactiveX - Operators

Introduction Each language-specific implementation of ReactiveX implements a set of operators. Although there is much overlap between implementations, there are also some operators that are only implemented in certain implementations. Also, each implementa

reactivex.io

RxJava의 많은 operator를 설명하는 페이지에도 마블 다이어그램을 이용해 여러 연산의 스트림 동작을 설명한다.

 

실제 여러 함수에 대한 마블 다이어그램

 

merge - 두개 이상의 Observable이 방출한 이벤트를 하나로 합침
map - Observable이 발행하는 요소를 변환

    @Test
    @DisplayName("Observable의 연산자를 이용해 스트림을 유동적으로 처리할 수 있다." +
            "filter 를 이용해 섭씨온도 영하만 필터링하고 " +
            "map을 이용해 화씨를 섭씨로 변환한다")
    void getMinusCelsiusTemperature() {
        Observable<TempInfo> seoul = getTemperature("Seoul")
                .filter(temp -> temp.getTemp()<0)
                .map(temp -> new TempInfo(temp.getTown(), ((temp.getTemp() - 32) * 5) / 9));
        seoul.blockingSubscribe(new TempObserver());
    }

댓글

Comments