리액티브 프로그래밍은 Future같은 객체를 통해 여러 결과를 제공 (future는 한번만 실행해 결과를 제공)
자바 9에서 java.util.concurrent.Flow 인터페이스에 발행-구독 모델(pub-sub 이라 불리는 프로토콜)을 적용해 리액티브 프로그래밍을 제공한다.
구독자(subscriber)가 구독할 수 있는 발행자(publisher)
이 연결은 구독(subscription)이라 한다
이 연결을 이용해 메세지(또는 이벤트로 알려짐)을 전송한다.
C3=C1+C2 스프레드시트 셀을 만든다 하자. (C1, C2 값이 갱신되면 C3에도 새로운 값이 반영된다) - c1, c2에 이벤트가 발생했을 때 c3을 구독하도록 한다. > Publisher<T> 필요 - Publisher<T>는 통신할 구독자(Subscriber)를 인수로 받는다
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package java.util.concurrent;
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);
}
}
Cell은 Publisher인 동시에 Subscriber이다. (셀 이벤트에 구독을 할 수 있다 / 다른 셀의 이벤트에 반응한다)
public class SimpleCell implements Flow.Publisher<Integer>, Flow.Subscriber<Integer> {
private int value =0;
private String name;
private List<Flow.Subscriber> subscribers = new ArrayList<>();
public SimpleCell(String name) {
this.name = name;
}
// Publisher 메서드
@Override
public void subscribe(Flow.Subscriber<? super Integer> subscriber) {
subscribers.add(subscriber);
}
//Subscriber 메서드
@Override
public void onNext(Integer newValue) {
this.value = newValue; // 구독한 셀에서 새로운 값이 생겻을 때 값을 갱신한다.
System.out.println(this.name + ":" + this.value);
notifyAllSubscribers(); // 값이 갱신됌을 모든 구독자에게 알림
}
private void notifyAllSubscribers(){ // 새로운 값이 있음을 모든 구독자에게 알리는 메서드
subscribers.forEach(subscriber -> subscriber.onNext(this.value));
}
}
@Test
@DisplayName("구독관계 확인 : C1은 Publisher / C3은 Subscriber > publisher 값이 변하면 subscriber에 이벤트가 간다")
void simpleCell() {
SimpleCell c3 = new SimpleCell("C3");
SimpleCell c2 = new SimpleCell("C2");
SimpleCell c1 = new SimpleCell("C1");
c1.subscribe(c3);
c1.onNext(10);
c2.onNext(20);
// C1:10
// C3:10
// C2:20
}
이 구조에서 (Subscription을 사용하지 않은 구조)에서는 publisher, subscriber의 동작을 연결하기 위해 참조객체로 List<Subsriber> subscribers을 두었다. c1은 publisher이면서(subscribe 명령어 수행) subscriber이다(onNext 수행)
> 여기서는 역압력(backpressure) 내용이 나오지 않았음
기존의 옵저버 패턴에 비해 새로운 API 프로토콜이 더 강력해진 이유 ; onNext, onError, onComplete와 같은 메서드로 데이터 흐름에서 예외가 발생하거나 종료되었을 경우도 제어하기 때문에
public class Jyami implements Account {
List<Platform> platforms = new ArrayList<>();
public void publishText(){
System.out.println("쟈미가 글을 발행했다");
notifyCrew("글 발행 전송");
}
@Override
public void subscribe(Platform platform) {
platforms.add(platform);
}
@Override
public void unSubscribe(Platform platform) {
platforms.remove(platform);
}
@Override
public void notifyCrew(String msg) {
platforms.forEach(crew -> crew.update(msg));
}
}
public class Blog implements Platform {
@Override
public void update(String msg) {
System.out.println("Blog 수신 : " + msg);
}
}
public class Instagram implements Platform {
@Override
public void update(String msg) {
System.out.println("instargram 수신 : "+ msg);
}
}
public class Facebook implements Platform {
@Override
public void update(String msg) {
System.out.println("FaceBook 수신 : " + msg);
}
}
public class Main {
public static void main(String[] args){
Jyami jyami = new Jyami();
Platform blog = new Blog();
Platform facebook = new Facebook();
Platform instagram = new Instagram();
jyami.subscribe(blog);
jyami.subscribe(facebook);
jyami.subscribe(instagram);
jyami.publishText();
jyami.unSubscribe(facebook);
jyami.publishText();
}
}
쟈미가 글을 발행했다 Blog 수신 : 글 발행 전송 FaceBook 수신 : 글 발행 전송 instargram 수신 : 글 발행 전송 쟈미가 글을 발행했다 Blog 수신 : 글 발행 전송 instargram 수신 : 글 발행 전송
이렇게 보면 간단한데 플로 인터페이스의 개념을 복잡하게 만든 기능은 역압력과 압력이다.(스레드 활용에서 필수적)
압력 (push 모델) : 매초마다 수천개의 메세지가 onNext()로 제한없이 전달된다면? > 어느정도의 메세지가 들어올지 숫자를 제한하는 역압력과 같은 기법이 필요하다.
역압력 (pull 모델) :자바 9 Flow API에서 발행자가 무한의 속도로 아이템을 방출하는 대신, 요청했을 때만 다음 아이템을 보내도록 하는 request() 메서드를 제공(Subscription 인터페이스)
2. 역압력
Publisher와 Subscriber 사이에 채널이 연결되면 첫 이벤트로 Subscriber 인터페이스의 onSubscribe(Subscription subscription) 메서드가 호출된다.
이 Subscription 객체는 Subscriber와 Publisher가 통신할 수 있는 메서드를 포함한다
옵저버 패턴과 pub-sub 패턴의 차이가 명확해 지는 부분은 이 subscription에 대한 부분 때문
3. 실제 역압력의 간단한 형태
한번에 한개의 이벤트를 처리하도록 pub-sub 연결을 구성하려면 다음과 같은 작업이 필요하다.
Subscriber가 onSubscribe로 전달된 Subscription 객체를 subscription 같은 필드에 로컬로 저장한다.
Subscriber가 수많은 이벤트를 받지 않게 onSubscribe, onNext, onError의 마지막 동작에 channel.request(1)을 추가해 오직 한 이벤트만 추가한다
요청을 보낸 체널에만 onNext, onError이벤트를 보내도록 Publisher의 notifyAllSubscribers 코드를 바꾼다. (보통 여러 Subscriber가 자신만의 속도를 유지할 수 있도록 Publisher는 새 Subscription을 만들어 각 Subscriber와 연결한다)
당김 기반 리액티브 역압력 : Subscriber가 Publisher로부터 요청을 당긴다(pull) = 리액티브 당김 기반(reactive pull based)라고 불린다.
리액티브 시스템이 가져야할 속성 : 반응성(responsive), 회복성(resilient), 탄력성(elastic) - 반응성 : 리액티브 시스템이 큰 작업을 하느라 간단한 질의의 응답을 지연하지 않고 실시간으로 입력에 반응한다. - 회복성 : 한 컴포넌트의 실패로 전체 시스템이 실패하지 않는다. - 탄력성 : 자신의 작업 부하에 맞게 적용하여 작업을 효율적으로 처리한다.
Java.util.concureent.Flow 관련된 자바 인터페이스에서 제공하는 리액티브 프로그래밍 형식을 이용하여 이 속성들을 구현할 수 있다.
자바 인터페이스 설계는 Reactive Manifesto의 마지막 속성인 메시지 주도(message-driven) 속성을 반영한다. > 박스와 채널모델
이 챕터의 목표 : Reacitve Stream 패턴의 이해와 reactive application의 설계 방법의 전반적인 이해를 위함.
4.4.1 Motivation
비동기(Asynchronous)와 리액티브 방법론 (reactive methodolgies)는 네트워크나 디스크 IO로 인한 쓰레드의 대기시간 낭비 대신 시스템의 리소스를 좀 더 효율적으로 사용하도록 해준다.
이런 스타일의 프로그래밍을 용이하게 하기위한 광법위한 기술이 존재하는데, java.util.concurrent.Future 부터 Akka와 같이 완전한 라이브러리들이 있다 (?)
Project Reactor는 매우 방대한 셋의 asynchronous workflow를 위한 연산자들이 있고, 이것들은 더이상 프레임워크에 의존하지 않으며 매우 많은 Reactive Streams model을 지원한다.
4.4.2 Understanding Reactive Streams
Reactive Stream은 처음에는 표준의 non-blocking back pressure(이게 뭐지) 을 기반으로 한 asynchronous stream의 표준을 제공하기 위해 만들어졌다. 여기에는 런타임 환경 (JVM and Javascript)뿐만 아니라 네트워크 프로토콜도 포함하려하는 목표가 있었다.
Reactive Streams의 범위는 목표를 달성하는 데 필요한 작업이나 엔터티를 설명하는 최소한의 인터페이스, 메서드 및 프로토콜 집합을 찾는 것이다. 그 목표는 non-blocking back pressure 이 있는 asynchronous streams data이다. (?)
그것은 어플리케이션 코드에서 라이브러리를 연결할 필요 없이, 상호자용을 할 수 있도록 허용해주는 multiple reactive composition 라이브러리들 사이의 운영 표준이다.
Reactive Stream의 통합은 주로 Publicsher<T> 나 Subscriber<T> 타입으로 복잡성을 숨기는 composition library (컴포지션 라이브러리)로 제공된다. Lettuce는 publisher를 Mono, Flux로 사용하는 Project Reactor를 사용한다.
Aynchronouse processing은 IO작업이나 계산과 같은 작업을 호추한 스레드에서 분리한다. handle이 그 return 타입이며, 주로 java.util.concurrent.Future와 동일하거나 비슷하다. 유사하게 single object나 collection, exception을 return 한다. asynchronously하게 fetch 한 결과를 가져온 결과를 기억하는 것은 주로 한 프로우의 끝이 아니다. 한번 데이터가 확보되면 항상 혹은 조건부로 추가 요청을 발행할 수 있다. Java 8이나 Promise 패턴을 사용하면 futuers의 chaining은 연속적으로 그 이후의 asynchronous한 request가 발생하도록 할 수 있다. 한번 조건처리가 필요하면 asynchronous한 flow을 interrupted 시키고 synchronized 해야한다. 이런 접근 방식은 가능하지만 asynchronous의 장점을 완전히 활용하지는 않는다.
반면 Publisher<T> 객체는 다양하고 asynchronous한 질문에 다른방향으로 대답한다 : Pull pattern을 Push patter으로 변환한다.
push : 데이터 변경시 변경이 발새된 곳에서 데이터를 보내주는 방식 (Mono, Flux) pull : 변경된 데이터가 있는지 질의 후 가져오는 방식 (클라이언트 요청 -> 서버 -> 응답)
Publisher<T>는 Futures처럼 single scalar value에 대한 emission 뿐만 아니라 emission sequences 심지어 infinite streams도 지원한다. 당신이 stream 작업을 시작하기만 하면 이 사실을 감사하게 여길 것이다. Project Reactor는 두개의 타입의 publisher가 사용된다 : Mono 와 Flux
Mono : 0부터 1까지의 event를 emit 한다. Flux : 0부터 N까지의 event를 emit 한다.
Publisher<T> 는 concurrency(동시성)나 asynchronicity(비동기성)의 특정 소스나 코드가 실행되는 방식에 편향되지 않는다. synchronous, asynchronous 둘다 ThreadPool 내에서 실행된다. Publisher<T>의 consumer는 실제 구현을 supplier에게 맡겨 나중에 supplier의 코드를 수정하지 않고도 변경이 가능하다.
Publisher<T>의 마지막 키포인트는 Publisher<T>를 가져올 때 처리가 되는 것이 아니라, Publisher<T>에 대한 observer가 subscribe하거나 신호가 보내지는 순간 프로세스가 실행된다는 것이다. 이것은 java.util.concurrent.Future와의 중요한 차이점이다. (Future는 created/obtained 가되는 순간 프로세스가 시작되기 때문이다.) 따라서 어떤 옵저버가 Publisher<T>를 subscribe 하지 않으면 아무일도 일어나지 않는다.
4.4.4. A word on the lettuce Reacitve API
모든 커맨드는 Flux<T>, Mono<T>, Mono<Void> 를 return 한다. 이것들은 Subscriber가 subscribe할 수 있다. 이 구독자는 Publisher <T>가 emit하는 아이템 또는 아이템의 시퀀스에 반응한다. 이 패턴은 Publisher<T>가 객체를 emit 할때까지 기다리는 동안 차단을 할 필요가 없어 동시작업(concurrent operations)에 용이하다. 대신, Publisher<T>가 어떤 futuer time에든 적절하게 반응하도록 준비가 되어있는 Subscriber 형태로 sentry를 만든다.
4.4.5 Consuming Publisher<T>
publisher를 사용할 때 가장먼저 고려할 일은 그들을 consume하는 것이다. Consuming a publisher의 의미는 subscribing을 의미한다.
@Test
@DisplayName("Consuming Publisher 예제 : emit 된 모든 항목을 subscribe 하고 print 한다.")
void consumingPublisher() {
Flux.just("jyami", "java", "javabom").subscribe(new Subscriber<String>() {
@Override
public void onSubscribe(Subscription s) {
s.request(3);
}
@Override
public void onNext(String s) {
System.out.println("Hello " + s + "!");
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
}
@Override
public void onComplete() {
System.out.println("Complete consuming!");
}
});
}
모든 구독자 또는 관찰자 (Subscriber or Observer)가 모든 이벤트에 대한 알림을 받고 완료된 이벤트도 받은걸을 확인 할 수 있다.한개의 Publisher<T>는 exception이 발생하거나, 그 Publisher<T>가 종료되었다고 onComplete()를 호출할 때까지 아이템을 내 보낸다. 그 이후에는 더이상 element가 emit되지 않는다.
subscribe의 호출로 한개의 Subscription이 등록되고 이것은 cancel을 허용하므로 더이상 이벤트를 수진하지 않는다. Publisher는 한번 Publisher<T>가 unsubscribed하면 구독 취소및 리소스 할당 해제(free resource)를 상호 운용할 수 있다. (?)
좀더 간단한 포맷의 Subscriber<T> 구현
@Test
@DisplayName("Subscriber<T> 의 더 간단한 구현")
void subscriberSimpleImpl() {
Flux.just("Ben", "Michael", "Mark").doOnNext(new Consumer<String>() {
public void accept(String s) {
System.out.println("Hello " + s + "!");
}
}).doOnComplete(new Runnable() {
public void run() {
System.out.println("Completed");
}
}).subscribe();
}
@Test
@DisplayName("Subscriber<T> 의 더 간단한 구현 - 람다사용")
void subscriberSimpleImplWithLambda() {
Flux.just("Ben", "Michael", "Mark")
.doOnNext(s -> System.out.println("Hello " + s + "!"))
.doOnComplete(() -> System.out.println("Completed")).subscribe();
}
Subscriber의 여러 연산자를 이용해서 element를 제어할 수 있다. take() 연산자는 관심이 있는 처음 N개 요소까지로 emit되는 수를 제한한다.
Hello Ben!
Hello Michael!
왜 여기서 Completed가 같이 출력이 안되는지를 모르겠다.
take 연산자는 기대하는 요소 수를() emit한 후 Publisher<T>에서 암시적으로 subscription을 취소한다.
Publisher<T> 에 대한 subscription은 다른 Flux나 Subscriber가 수행 할 수도 있다. 커스텀한 Publisher를 구현하지 않는한 항상 Subscriber를 사용하라.
그리고 항상 error handler를 올바르게 구현하는 것을 권장한다. 특정시점에서 일이 잘못될 수 있다 (스택트레이스가 쌓여서) 완벽하게 구현이 된 subscriber는 이벤트에 반응할 수 있게 onCompleted, onError 메서드를 모두 선언하자
4.4.6. From push to pull
위에서 햇던 예제는 publisher가 blocking, non-blocking execution에 대해서 별다른 의견없이(추가 없이) 설정되어있는 방법을 기술하였다. Flux<T>는 명시적으로 Iterable<T>로 변환하거나 block() 메서드를 이용해서 synchronized(동기화) 할 수 있다. block() 호출은 피하자. block()을 호출하면 애플리케이션에 대한 reactive chain의 모든 non-blocking 적인 장점이 사라진다.
@Test
@DisplayName("block() 연산자 : async -> sycn")
void blockOperator() {
String last = Flux.just("Ben", "Michael", "Mark").last().block();
System.out.println(last); // Mark
}
blocking 호출은 publisher의 체인을 synchronize로 사용되게 할 수 있고, 평범하고 잘 알려진 Pull 패턴으로 돌아가는 방법을 찾는다.
toList 연산자는 모든 emmited된 요소들을 모으고(collect), 그리고 그 리스트를 BlockingPublisher<T> 타입으로 패스한다.
4.4.7 Creating Flux and Mono using Lettuce
publisher를 설계하는 방법은 많이 있다. 당신은 이미 just(), take(), collectList()를 이미 보았다. Project Reactor documentation 의 참조에 따르면 Flux와 Mono를 만드는데 사용 할 수 있는 더 많은 메서드가 있다.
Lettuce publisher는 초기화나 체이닝 작업에 사용할 수 있다. Lettuce publisher를 사용하면 non-blocking 동작을 확인 할 수 있다. 이는 모든 I/O 및 커맨드 처리가 netty의 EventLoop를 사용해 aynchronously(비동기적)으로 처리되기 때문이다.
key에서 value를 가져오려면 GET 연산이 필요하다. (subscribe자리에 Consumer를 넣었다.)
@Test
@DisplayName("lettuce publisher를 사용하고, 여기서 get 연산자를 통해 redis의 key에 따른 value를 가져올 수 있다. ")
void LettucePublisher() {
commands.get("key")
.subscribe(System.out::println);
}
이것의 실행은 asynchronously(비동기적으로) 처리되며, Netty EventLoop Thread에서 작업이 완료되는 동안 호출하는 스레드(invoking thread)는 프로세스내의 다른 일을 처리할 수 있다. 분리된 특성으로 인해 호출한 메서드(calling method)는 Publisher<T>의 실행이 완료되기 전에 그대로 둘 수 있다 (?).
Lettuce의 publichser는 연결된 컨텍스트(context of chaining) 내에서 여러개의 키들을 asynchronously(비동기적으로) 로드하는데 사용될 수 있다.
작년에도 회고록을 31일에 맞춰 썼는데, 올해도 결국에는 31일에 쓰는구나...ㅋㅋㅋ 아니 사실 이거 쓰기 전에 취업 관련 글 하나 쓰려했는데 시간 관계상 그건 신년에 쓰는 걸로 하자 크크... 와 그나저나 21년에는 24살이구나 시간 빨라...
올해 코로나 때문에 취업이 어려워졌단 소리가 엄청 많아졌는데, 덕분에 나도 올해에 취업으로 희로애락을 다 경험한 것 같다..ㅋㅋㅋ 올해 회고는 거의 취준생 일기일 것 같지만 그래도 시작해보자 후후 (쓰는 게 어디야 쓰는 게..)
와.. 지금 보니 이때 열정 가득했었구나.. 새삼 회고록 쓰면 약간 1년 일기장 같긴 한데 새록새록하다
[1월 - 2월] 인턴십 지원과 여러 활동들
인턴십 지원과 합격
의도한 건 아니지만 나름 내 대학교 로드맵에는 순서가 있었는데ㅋㅋ 1학년 : 교내 개발 동아리 / 2학년 : 교외 개발 동아리 / 3학년 : 회사 연계 개발 관련 대외활동 / 4학년 : 회사 인턴십 및 구직 이렇게 대학교 생활이 지나갔다.
따라서 인턴십 준비는 19년도 12월부터 했었고, 어쩌다 보니 교수님 추천, 지인 추천, 직접 지원 등 여러 경로로 인턴십을 지원을 했었던 기억이 난다. 이에 따라 대부분 1~2월에 서류 및 코딩 테스트 결과가 났었다.
결과 발표가 쏟아졌던 2월달 크으으..
20년도 1~2월 당시에는 정말 취업 연계형 생각 없이 체험형 인턴으로 스펙을 쌓기 위해 도전을 했었기 때문에 코딩 테스트나 면접 준비나 전부 전무했다. 그렇지만 감사하게도 면접에서도 나를 좋게 봐주셔서 (와 이때 생각해보니 면접 준비 하나도 안 하고 전날에 벼락치기해서 인턴 첫 면접을 갔었네.. 패기..) 20년도 상반기 네이버에서 인턴을 할 수 있게 되었다
그래서 사실 입사 전이니까 2월은 잘 쉬고 놀고 그랬어야 했지만.. 역시나 추진력 하나는 최고인 재미는 실수를 하고 마는데...
백엔드 외주
그렇다... 2월 말까지 마감인 외주를 하고 있었다..ㅋㅋㅋ 같이 오픈 핵 + 중국 해커톤을 갔었던 오빠가 자바 라이브러리를 API로 만드는 작업을 부탁해서 했던 외주였다. 이때 GCS연동 + 외부 라이브러리 연동이 주 과제였는데, 와.. 이때 테스트 코드의 중요성을 확 느꼈었다.
사실 스프링으로 그냥 간단하게 controller만 만들고 service단에 자바 라이브러리를 넣으면 되겠지 하고 객체 관계나 클린 코드 신경 쓰지 않고 내 마음대로 그냥 스파게티 코드를 막 짰었다.. 그런데 그러다 보니 내가 모르는 에러가 막 발생했다. 결국 정신 차리고 테스트 코드를 짠 후 코드를 하나하나 리팩터링 하면서 객체가 하나의 역할을 가지게 내부적으로 수정했었다. 이때 유지보수나 객체지향을 무시하고 짠 코드의 위험성을 체감했었다.
그래서.. 사실 인턴십 면접 보기 직전에도 이 외주 작업 코드 수정하고, 면접을 보고 입사 직전까지도 이 외주 작업을 계속하느라 정신이 없었던 기억이 난다. 지금은 내가 만든 서버를 사용하지 않는 걸로 알고 있는데, 회고 쓰면서 찾아보니 검색도 되고.. 오올 학생 스타트업으로 열일 하는 것 같다.
네이버 AI 버닝 데이
그리고.. 맞다.. 네이버 AI 버닝 데이에 갔었다.. 진짜 3학년 때 중국톤 이후로 해커톤 다시는 안 해!! 이랬는데. 는 무슨 연례행사로 하나보다. (그리고 버닝 데이 끝날 때도 해커톤 안 해 -ㅅ- 이랬지만..ㅋ)
사실 AI가 주인 대회라서 내가 할 일이 별로 없을 줄 알았는데 아아... 매번 해커톤을 가면 할 때마다 하나씩 헤매는 부분이 생긴다. 기억하기론 우리 팀 AI 엔지니어 언니가 작성한 python 코드를 스프링에서 돌릴 때 디버깅이 힘들었던 문제였던 것 같은데 기억이 잘 안 난다.
그리고 이때 Naver Cloud Platform 크레딧을 제공해줘서 이 플랫폼에 익숙해지느라 애먹기도 했다 (이때의 경험 덕에 요즘 NCP로 돌리는 서버가 한대 있긴 하다)
검검은 개발자룩의 정석이지ㅋ
진짜 해커톤이 너무 신기한 게 어떻게든 완성을 하긴 한다..(그리고 생산성이 중요한 해커톤에서 다시는 스프링 안 쓴다고 했지만... 결국은 항상 스프링 쓴다)사실 이 대회는 네이버 실무진과의 이야기를 통해 사람에 따라 취업 기회도 주는 성격이 있었는데, 이미 인턴십 합격이 된 상황이라서 그냥 기념품을 잘 받아왔다 >< (이때 받은 맨투맨 일코 하기가 좋아서 너무 잘 입고 다닌다)
그리고 이때 레크리에이션 때 어쩌다 보니 경품으로 에어 팟을 받게 되었는데 크으... 이미 하나 쓰고 있는 게 있기에 당근 마켓 했다ㅋ
막 그렇게 엄청 심혈을 기울인 행사는 아니었지만, 새삼 1~2월 사이에 많이도 했다는 생각이 든다..ㅎㅎ 이때 나 정말 알고리즘 못하는구나...라고 생각했었고 버스 탔었는데 (아련..) 지금도 알고리즘은 자신이 없다ㅠㅠ
이화 동산 커미터스
이전 DSC Ewha Lead 회고에도 썼지만, 이때 당시 DSC 몇 명의 멤버들과 일일 커밋 그룹을 만들었었다. 이때 커밋 기록 관리 툴로 슬랙과 웹을 사용했었는데, 웹 같은 경우 기존에 비슷한 활동을 하시던 그룹의 코드를 포크 해와서 우리 그룹에 맞게 커스터마이징 하는 작업을 했다. (관련 글 : jyami.tistory.com/123?category=865823)
기존 코드가 장고로 되어있었고, 도커로 말려있었는데 이때 환경 세팅하는 부분에서 엄청 애먹었었다. 장고도 익숙하지 않았고 도커도 익숙하지 않아서였는데, 흠 뭔가.. 근데 요즘 느끼는 건 가면 갈수록 프레임워크든 뭘 사용하는지는 점점 상관이 없어지는 것 같다. 점점 구글링에 능숙해져서 그런가ㅋㅋㅋ 모르면 그냥 찾아서 배포하고 만들고 삽질을 하면 된다는 마인드가 생겼달까
[ 3월 - 6월 ] 코로나 그리고 4학년 1학기와 첫 인턴십
코로나 붐
2020년 3월 급격하게 코로나 확진자가 늘어났다. 확진자가 늘어나면서 여러 활동들을 중단하게 되었는데 대표적으로는 학교도 모두 온라인으로(DSC 활동을 거의 못하게 되었다..), 회사도 재택근무를 했다. 학교는 대면 수업으로 해도 회사와 병행이 가능하게 조절을 잘해두고, 입사를 기다렸는데 입사일도 미뤄지고 입사도 집에서 했다. (그리고 쟈미가 입사할 무렵 코로나가 심해지고 재택근무로 돌아가는 현상은 이후에도 반복되는데...)
그리고 무엇보다. 인턴십을 기대하고 새로운 집도 구해놨는데 재택근무라 본가에 있는 게 더 편해서 월세가 너무 아까웠음... ㅠㅠ
첫 인턴십
처음 했던 인턴십 내용은 아래 글에 너무 자세히 적어두기도 했고ㅋㅋ 다들 너무 많이 읽어주셔서 링크로 대체한다.
학교와 회사를 같이 병행하게 해 두긴 했지만 그래도 해야 할 일은 있었다. 어찌 됐든 졸업을 하기 위해 기존에 하던 졸업 프로젝트를 다 했어야 했는데 이때 친구들한테 너무 감사했다... 퇴근하고 작업하다 보니 코딩도 많이 못하고 참여도 많이 못했는데 덕분에 잘 마무리했다...ㅎ 진짜 대학생활 첫 프리라이딩이었던 것 같...ㅋㅋㅋ
이 졸업 프로젝트 때문에 퇴근하고 코딩하고 배포하고.. 안드로이드 xml도 짜고 별 경험을 다했는데ㅋㅋㅋ 회사와 학교 병행하기 정말 힘들다는 걸 알았다ㅠㅠ 회사일에만 집중하고 싶은데 그러지 못해서 슬펐다.. 그렇지만 이걸 안 하면 졸업을 못해서 취직을 못하닠ㅋㅋㅋ (그치만 이것 역시 같은 실수를 반복하게 된다..)
결국 4학년 1학기는 총 14학점 중 패논패만 11학점으로ㅋㅋㅋㅋㅋㅋ 엌ㅋㅋㅋㅋ (회사 학교 병행 꿀팁 : 패논패) 그리고 졸프는 A 학점으로 마무리하게 된다. 원래는 조기졸업도 생각을 했었는데, 조기졸업을 위해 학점을 더 들으면 인턴십에 무리가 있을까 걱정돼서 조기졸업은 미루게 되었다.
[ 6월 - 8월 ] 짧은 취준과 꿀 같은 휴식
우울감 극복
6월 중순 전환면접을 보자마자 느낌이 안 좋아서ㅋㅋㅋㅋ 사실 그날 바로 이력서를 업데이트했다ㅋㅋㅋㅋㅋ 원래 없던 영어 CV도 작성하고, 기존 인턴십 이력도 추가하고
이력서 업데이트뿐만 아니라 여러 곳을 더 넣으면서 최대한 우울한 감정에 빠지지 않으려 했다. 처음에는 오랫동안 기대하고 노력한 일을 실패했다는 생각에, 우울한 감정으로 많이 울고 힘들어했었지만, 생각지도 못한 이유 덕분에 빠르게 회복했다.
떨어졌다는 상실감이 사실 투지력으로도 불타서ㅋㅋㅋㅋ 공부도 꾸준하게 다시 시작하고, 이곳저곳 이력서를 넣었다. 그런데 이력서를 넣으면서 내가 더 원하는 곳에 도전할 수 있는 기회가 생긴 거구나 라는 생각이 문득 들었다. 그래서 실제로 이력서를 넣은 4곳 중 3개 모두 최종 합격까지 가게 되었고, 내 가치를 봐주는 곳이 많다는 자신감이 생기면서 극복하게 되었다.
6월 중순에 네이버 인턴십 면접을 봤는데.. 새삼 놀라는 나의 실행력
당시 그래서 한 달 반 정도 취준을 위한 공부를 했던 것 같다. 매일매일 리트코드 챌린지를 이용해서 알고리즘 연습을 하고, 총 5~6번의 면접을 위해 그때그때 내가 사용했던 프로젝트의 기술 스펙을 돌아보고 컴퓨터 공학 지식을 한번 더 점검하는 시간을 가졌다. 사실 이 부분도 굉장히 기억에 남는다. 만약 그냥 취업을 했다면, 취업을 위한 최소한의 컴퓨터 공학 지식 공부도 안 했을 텐데, 이 기회를 통해 공부한 게 좋았다. 그동안 4년간의 공부를 정리하게 되었고 이때 쌓아둔 기반은 앞으로의 다시 깊게 공부할 기초에 대한 아웃라인이었기 때문에 하길 잘했다는 생각이 들었다.
짧은 취준이었지만 기술적으로도 정신적으로도 정리할 수 있었던 시간이었다. 사실 뭔가 큰일에서 엎어지고 극복한 게 이번이 처음이 아니었다. 대입에서도 내가 장시간 준비했던 원하는 학교에 불합격을 했는데, 오히려 전화위복으로 이화를 가게 되었고 더큰 기회를 얻게 되었다. 그런데 되돌아보니 하나의 징크스처럼 지금도 그런 게 아닐까?라는 생각을 하게 되었다.
길고 길었던 카카오의 절차
사실 이 부분에 대한 글을 쓰고 있었는데 완성을 하지 못해 회고부터 쓰게 되었다..ㅠ 이 글 다쓰면 회고에도 참조해서 다시 넣어야지ㅎㅎ
나는 취준을 하면서 공채에 넣을 생각이 1도 없었다. 공채에서는 아무래도 개개인의 활동에 대한 맞춤 질문보다는 좀 더 정량적으로 평가하기 위한 요소들이 훨씬 많을 것이라는 예상이 있었기 때문이다. 이런 평가가 나의 가치를 제대로 봐줄 수 있을까라는 의구심과 여러 IT기업의 공채 공고가 뜨기까지 기다리지 못하는 조급함이 있었다.
내가 서류를 넣은 4곳 중 가장 가고 싶었던 곳은 카카오였다. 사실 그래서 최종 합격 발표가 난 나머지 두 곳이 시기는 더 빨랐지만, 가장 가고 싶었던 곳이 여기었기에, 두 곳 모두 계약서 작성을 거절하고 여기 면접에만 집중했었다.
지금봐도 너무!!!! 많았던 카카오 입사 절차
나는 상시채용으로 기반서비스개발팀에 지원을 하게 되었고, 신입의 경우 상시채용으로 입사하면 계약직으로 3개월 일하고 이후에 평가를 한 번 더 받는다는 페널티가 있었지만 그런 페널티를 모두 무시하고도 너무 가고 싶었다..!!!!
서류 전형 => 코딩 테스트 => 원격 인터뷰(전화 인터뷰) => 1차 인터뷰 => 2차 인터뷰 => 합격
절차는 위와 같았는데, 전화 인터뷰는 1시간, 1차 인터뷰는 2시간, 2차 인터뷰는 1시간.. 총 4시간의 면접을 통해 합격을 하게 되었다. 지금 생각하면 같이 일하시는 분들이 인터뷰를 봐주신 거였는데 다들 해주시는 배려가 너무 감사했다.
사실 카카오에 가고 싶었던 이유 중에 하나로 면접관의 분위기가 있었다. 나는 인터뷰는 면접관이 지원자에 대해 평가를 하는 자리뿐만 아니라, 지원자가 그 회사에 대해 평가도 같이하는 것이라고 생각하고 있다. 그런데 카카오가 가장 면접관분들의 질문이 가장 컴퓨터 지식에 대해, 그리고 내 활동에 대해 깊게 물어봤기 때문에 인상이 좋았다. 한 가지 기술이라도 내가 기술을 사용할 때 어떤 부분을 고려하고 사용하는지, 사용하는 기술에 대해 이론적으로도 올바르게 알고 있는지 대부분 이런 질문이었는데, 그만큼 더 공부하고 알아야 하는 팀이라서 더 깊은 질문을 하시는구나라는 느낌을 받았다. 그리고 틀리게 대답한 질문이면 다음 면접에서 혹시 이 부분 다시 대답해보실래요? 이런 식으로 개선의 기회도 주는 게 너무 감사했다.
이때 모든 면접을 화상으로 봤는데 화상으로 보다 보니 한 번쯤은 가보고 싶었던 카카오 오피스도 못 가봤다 흑흑.. (그래도 합격하고 입사해서 갔으니 괜찮다ㅎ) 그치만 코로나 시대 취준생으로써 앞으로 면접을 직접 대면해서 보는 게 더 어색할 것 같다. 화상으로 하는 면접이 너무 익숙해졌다.
이외에도 받은 느낌이 너무 많은데 이건 나중에 쓸 취준 글에서 쓰기로 하자ㅋㅋ
합격과 함께 꿀 같은 휴식 - 나 홀로 제주도 여행
결국 가장 원하던 곳에 합격을 하고 저번에는 못했던 리프레시하는 시간을 가졌다. 그동안 쉼 없이 달려 번아웃이 온 상태였기 때문에 휴식은 갖고 싶었기 때문이다. 나는 학교를 다닐 때도 휴학 없이 방학에도 대외활동으로 시간을 많이 보냈었는데, 그 이유는 뭔가.. 쉬려고 해도 쉬지 않고 내가 뭔가를 또 하고 있을걸 알았기 때문에 휴학도 휴식도 그냥 안 가졌었다ㅋㅋㅋㅋ
그렇지만 역시 8월에도 코로나 때문에 누군가를 만나기는 부담스러웠다. 그래서 가고 싶었던 유럽여행도 가장 마음이 편한 시기인 취업 확정된 상태인 입사 전 한 달 동안 꼭 갔다 올 거라고 버킷리스트에 담아두었었다. 그렇지만 코로나...ㅠ 괜히 해외로 여행 갔다가 코로나 걸리면 입사에만 문제가 생길 테니 포기했다. 대신 조심하면서 한 번쯤은 해보고 싶었던 국내 혼자 여행을 했다.
혼자 제주도에 가서 혼자 오겹살도 먹고, 서핑도 배우고 이래저래 많이 돌아다니면서 힐링을 했다. 제주 바다는 언제 봐도 좋았다.
이때 태풍이 오던 시기라서 태풍도 빡세게 경험했었다. 원래 태풍 때문에 하루 내내 호텔에 있을 생각을 했고, 그래서 그날은 하루 종일 호텔에서 롤 하려고 노트북도 가져갔다!!! 근데 태풍 때문에 호텔이 정전이 됐곸ㅋㅋㅋㅋㅋㅋ 하... 결국 계획대로 하지 못했다ㅠ
면허가 없는 뚜벅이라서 조금 힘들기도 했지만 나름의 매력이 있었다 처음 만나는 언니가 태풍 오는 날 위험하다고 호텔까지 데려다 주기도 하고 뚜벅이라서 힘들었지만 뚜벅이라서 스펙타클했던 경험을 했다.
제주도 외에는 입사 전에 한 일이 게임밖에 없다ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 원래도 롤을 좋아했었는데, 이 시기에 같이하는 게임 친구들도 생겨서 진짜 매일 오전 6시에 자고 낮 3시에 깨고... 겜창 인생을 반복했다ㅎ 랭크했으면 회고록에 적었을 텐데 아쉽게도 맨날 5인 큐로 일반 돌리다 보니 op.gg에 총판수만 늘어났다 (하... 이때 학교 졸업준비 좀 했어야 했는데...)
엔젤 핵 참가
아 생각해보니 취준 중에 엔젤핵도 나갔었네, 엔젤핵이 올해는 리모트로 진행이 되었는데 기존에 중국 해커톤을 했던 소현이 원종오빠 + 멋사에서 같이 촬영팀을 했던 현석오빠 + 같이 자바 봄 스터디를 하고 있는 민형 오빠 이렇게 다섯이 참가하게 되었다. (이때 스프링 시큐리티를 조금 보게 되었는데 진짜 아직도 모르겠다ㅋㅋㅋ)
위에 네이버 버닝 데이 할 때만 해도 해커톤은 절대 안 한다!! 이러고 있었는데, 그치만 다들 오래 본 사람들이라서 친한 사람들끼리 나가는 해커톤이 또 나가고 싶었던 건지 하핳.. 또 하게 되었다.이 서비스는 언제 마무리하지... 해커톤은 하면 할수록 마무리 안된 코드만 늘어나는 것 같아 슬프다 아휴..
우리는 처음에는 온라인으로 하다가 마지막 2일 다 같이 에어비앤비에 모여서 작업했었다. 당시 타임랩스도 찍고,, 다들 잠도 못 자고ㅋㅋㅋ 항상 해커톤은 할 때마다 느끼지만 프론트엔드가 고생하는 것 같다. 뭔가 항상 바빠 보인달까
엔젤핵 할 때 재밌었던 것 중 하나는 이때 온라인으로 하다 보니 운영진분들이 액티비티도 온라인으로 준비하셨었다. 그래서 온라인 게임 점수를 기반으로 상품을 주곤 했는데, 크롬 공룡 게임에 미쳐서...(그냥 게임이면 미치는 듯) 개발도 안 하고 계속 공룡 점프나 시켰다.
[ 9월 - 12월 ] 카카오 입사 그리고 학교 졸업?
카카오에서의 생활
그렇게 게임만 하고 살던 와중 입사가 가까워져 왔다. 근데 왜 위에 말한 것처럼 왜 내가 입사할 때면 코로나가 심해지는 걸까ㅠ 인터뷰도 원격으로 보고, 입사도 원격으로 하게 되었다. 카카오에서 했던 원격 입사에서 했던 온보딩 프로그램 진행할 때 받은 워크북이랑 이것저것 굿즈들❤️ 너무 좋았다ㅎㅎ 아 면접 때는 몰랐던 내가 일 할 파트도 알게 되었는데 카카오톡의 채팅 부분에 해당하는 톡 메시징 파트에 가게 되었다. 가장 핵심인 서비스이고 그런 만큼 정말 봐야 할 것도 알아야 할 것도 많다는 걸 입사 처음에도 실감했지만 지금은 더더더더더 실감하고 있다..ㅠ 공부하자 제발
온보딩때 받은 굿즈들
그렇게 풀 재택근무를 한 한 달 정도 했었다. 그리고 약 한 달 후 코로나가 꽤 괜찮아져서 순환근무를 2주 정도 하다가, 풀 출근을 2주 정도 했다. (그러다 다시 심각해져서 요즘과 같이 일확진자 1000명대가 되었다.)
사실 재택근무만 할 때는 적응하기 힘들었다. 여쭤보는 건 언제나 고민이었다. 너무 쉬운 걸 물어보는 건 아닐까, 멘토님이 실무로 바쁘신데 내가 귀찮게 하는 건 아닐까..? 하는 생각이 있었기 때문이다. 그치만 항상 물어보면 너무 친절하게 알려주셨다. 그리고 내가 본 분 중에 진짜 가장 대단한 분을 만나게 된 점에 너무 좋았고 멘토님은 모르겠지만 많이 존경하고 있다ㅋㅋㅋㅋㅋ 하ㅏ... 진짜 대단... 그렇게 되고 싶다
어쨌든 확실히 신입은 출근을 하면 좋은 것 같긴 하다. 출근했을 때 이래저래 파트분들과의 일상에 조금씩 적응하게 되고 소속감이 느껴지기 때문이다. 그래서 누구보다도 사실 출근을 하고 싶었는데... (이번 연말 대응에도 너무 회사에 가보고 싶었는데..) 안타깝게도 요즘 너무 심하다...
나의 재택근무 환경 (왼쪽은 회사코드라 블라인드)
카카오 상시채용 신입으로 들어가 계약직으로 지낼 때도 마찬가지로 인턴 과제를 받아서 수행했다. 네이버 때와는 다르게 파트와 너무 연관된 내용이 많아 자세히 적을 순 없지만, 생각보다 새로운 도전을 많이 해보았다. 그리고 반성도 많이 했었다.
이번에 새로웠던 건 네티 프로그래밍 + 코틀린을 사용했던 것이었다. 벌써부터 공부해야 할까? 싶었던 4 계층을 이번 기회에 책도 보고, 기존 실무 코드도 보면서 익힐 수 있었는데 아직도 헷갈린다ㅠ 이 파트에 있으면서 진짜 공부할게 엄청 많다는 걸 느낀다. 면접 때 물어본 스레드나, 비동기 프로그래밍, 동기화 등 다소 어려웠던 개념들을 집중적으로 여쭤보신 이유가 있었구나 싶었다. 그때 이론적으로만 열심히 했던 대답들을 코드로 실제로 threadExecutor나 sychronized 등 사용되는 코드를 보니 내가 이걸 건드려도 되는 걸까 싶었다.
그리고 처음 써본 코틀린에 대한 느낌은 와 진짜 편하긴 하다!!! 근데 아직 자바보다 덜 익숙해서 내년에 코틀린 인 액션을 읽으면서 안티 패턴을 피하고, sealed class나 suspend function, 코루틴 등 코틀린을 코틀린답게 사용하는 방법을 공부하려 한다. 개인적으로 정말 좋았던 건 생성자가 너무 편했다 내가 만든 과제 서버도 코틀린 + 스프링으로 짤까 고민도 했지만 평가가 걸린 만큼 익숙한 언어로 구현하는 게 나을 것 같아 자바 + 스프링을 사용했는데, 코틀린 <=> 자바 호환도 되게 잘 돼있다. 꼭 코프링해봐야지+_+
그리고 이번에도 역시 k8s 기반으로 배포를 했는데, 이전에는 helm을 사용해서 오브젝트들을 관리했는데 이번에는 파트 내에서 kustomize를 사용하고 있어서, 나도 kustomize로 작성을 한번 해보는 걸 도전해보았다. 약간 개인적인 느낌은 으음,, 비유가 올바를지는 모르겠지만 helm은 약간 jsp에 데이터 바인딩하는 것처럼 주어진 템플릿에 값을 주입해 넣는다면, kustomize는 자바 오버라이드처럼 설정 값에 따라, 오브젝트 공용 설정은 미리 베이스로 해 두고 수정이 필요한 부분만 설정값을 오버라이드 해서 넣는 느낌이었다. 근데 사실 어떤 툴이든 특성에 맞게 잘만 사용하면 되는 거니까 +_+ 이번에는 k8s를 사용할 때 클러스터 내 네트워크도 구성하면서 한 단계 성장한 느낌이 든다.
약간의 슬럼프 그렇지만 정규직 전환 합격
내 이름표와 사원증!! 야호
다들 몰랐겠지만 사실 슬럼프가 왔었다. 사실상 인턴생활에 올 한 해를 전부 보내버렸고, 인턴이다 보니 제한된 권한도 있었고 실무로부터 조금은 떨어져 있는 과제를 받다 보니 실무에 기웃거리지만 혼자서만 코드를 짠다는 것에 회의감을 느꼈기 때문이다. 아무래도 나 혼자서 커밋하고 머지하는 코드는 정체될 수밖에 없고 한계가 있을 수밖에 없는데, 혼자 하는 프로젝트를 계속해서 하다 보니 힘들었었다.
만약 다른 친구들처럼 상시채용이 아니라 공식적인 인턴 모집으로 같이 인턴 생활하는 하는 친구들이 있었다면 좀 더 의지할 곳이 있지 않았을까. 많은 다른 개발자들처럼 공채를 준비했으면 같이 입사한 동기들도 있고 계약직이라는 불안정한 상황 없이 바로 정규직이니까 조금 덜 불안하지 않았을까. 와 같은 만약을 계속 생각하며 아쉬워했었다. 원래 나의 행동에 후회를 하지 않고 그때그때 잘한 선택이라고 믿고 추진하는 편인데 나에게 상시채용 + 인턴이 최선의 선택지였고 지금 충분히 잘하고 있다는 걸 알고 있음에도 자꾸만 다운되었다.
사실 이렇게 다운되는 경우 여러 사람들을 만나고 기운을 받아 다시 동기부여를 하는 타입인데, 코로나로 인해 그러질 못했어서 더 슬펐다. 그러다 보니 계속 이야기를 할 수 있는 게임 친구들과의 디코에 들어가게 되고 그러다 보니 이전보다 개발 공부에 투자하는 시간이 적어지고 게임에 투자하는 시간이 많아지는 악순환이 반복되었다.
그래서 정규직 전환 면접 직전 가장 멘탈이 불안한 시기에는 본가로 올라와서 일부러 가족들과 함께 보냈다. 그래도 꿋꿋이 버티고, 업무시간에는 딱 집중을 하면서 살다 보니 다행히도 면접에서 좋은 인상을 주었는지 전환면접에 합격하게 되었다. 당시 합격에 대한 기쁨으로 모든 사람들에게 알리고 축하도 많이 받았는데 이때 슬럼프에서 탈출했다ㅎㅎ 아무래도 직장이나 소속에 대한 불안정성이 내 슬럼프의 가장 큰 원인이었던 것 같다.
유튜브 시작
그렇다.. 유튜브를 시작했다. 사실 시작은 정말 소소했다. 재택근무하는 타임랩스를 인스타 스토리에 올렸는데 주변 친구들이 유튜브 각?! 유하~! 이러고 보내서 호옹.. 진짜 해봐?! 이러고 패기롭게 9월에 시작했다ㅋㅋㅋㅋㅋ
그치만 점점 전환면접으로 바빠지면서 11월 12월은 업로드 주기가 조금 느려지게 되었는데, 호오,,, 신기하게도 많은 분들이 사랑해주시고 계시다 (현시점 888명!!). 위에 링크 사진을 보니.. 썸네일도 배경도 대충 해서 올린 거라 이것도 유튜브 향으로 바꿔야 하는데 헤헷 중요한 건 아니니 미루기로 하자. 개인적으로 유튜브 찍으면서 좋았던 건 코드윗미나 스터디 윗미 찍을 때 뭔가 강제적으로 공부하게 된다ㅋㅋㅋㅋㅋ 굿... b
+ (2020년 1월 1일) 이전에 후배가 그려준 그림이 있어서 프로필 사진을 바꿨다 >.0
졸업을 위한 도전 학기 프로젝트
이번 회고는 쓰다보니 복선이 많다. 회사다니면서 학교 플젝하면 힘들다라고 했는데, 이번 계약직 기간에도 마찬가지로 학교플젝을 하고 있었다ㅋㅋㅋㅋ 이렇게 된 이유는 참... 코로나 때문이다.
도전 학기 지원 당시 나는 전공 6학점이 남은 상황이었고, 하반기에 코로나가 잠잠해지고 학교에서 대면 수업을 하지 않을까?라는 생각이 있었다. 그렇기 때문에, 회사와 학교를 병행하면 학교의 대면 수업을 절대 못 갈 것 같다 생각했고, 그렇기에 혼자서 프로젝트를 설정하고 그걸 학기 내에 완수하는 도전 학기제를 선택했다. (그래서 덕분에 회사 퇴근하면 이거 개발하고 그랬다..ㅠ 갈갈각랄... 8월 한 달 내내 롤 하지 말고 이걸 했어야 했는데ㅋ) 근데 후회했던 건, 사실 코로나가 이렇게 심해질 줄 알았다면 어차피 비대면일 테니 그냥 대충 수업 듣고 F만 안 맞았으면 됐겠네?라는 아쉬운 점이 있다는 것이다ㅋㅋㅋ. 그랬다면 회사일에 좀 더 집중할 수 있었을 테니!
내가 선택한 도전 학기 프로젝트는 위에서 했던 이화 동산 커미터스의 확장판으로 이화인을 위한 개발 커뮤니티를 만드는 프로젝트를 진행하였다. 개발은 스프링, 리액트로 백 + 프론트 모두 했으며 배포도 헤로쿠로 했으나 현재는 에러를 발견해 서버를 내려둔 상태이다.
현재 커밋 기록 가져오는 작업에 대해 배치성 작업임에도 불구하고 API 형태로 구현되어있어 보수를 해야 하며, 스프링 시큐리티에 미숙해 에러 핸들링 부분에 오류가 있던걸 배포 직전에 발견했다. 또 프론트엔드 역시 어... 되게 해커톤식의 코드로 구현해두어서 마음에 들지 않아 실제 유저를 대상으로는 배포를 하지 않았다. 현재 주변 지인들로 베타 버전에 대한 피드백만 받았으며 음.. 실제 배포는 보수해서 내년 2월에 할 수 있으면 좋겠다ㅋㅋㅋㅋ
대시보드 캡쳐본 (원래는 세로로 스크롤해서 보는것..)
이 프로젝트로 전공 6학점을 받게 되었고 나는 졸업 이수요건인 140학점을 모두 채울 수 있었다. 그래서 나의 대학교 학점은 4.3 기준 3.85 / 4.5 기준 4.03으로 마무리하게 되었다. 사실상 4학년 1학기 2학기는 모두 회사생활로 인해 패논패 수업을 들었기 때문에 내 학점은 사실상 3학년 때 완성을 시켰다.
그렇지만 나에게는 한 가지 복병이 더 남아 있었는데...
결국 수료...ㅎ 졸업은 내년ㅋㅋㅋㅋㅋㅋ 사진이나 찍자
그렇다... 조기졸업도 생각했지만 결국 칼 졸도 못하고 수료를 하게 되었다. 그 이유는 토익이다ㅎ 우리 학교 컴공 졸업요건에는 토익 700을 넘어야 하는 기준이 있다. 그러나 하하.. 매번 영어는 가장 후로 미루고 미루다 보니 항상 회사 + 여러 학교 프로젝트 + 개발 공부에 우선순위가 밀려 토익공부를 안 했다.
토익공부를 하나도 안 한 채로 혹시나 하는 마음으로 토익시험을 한번 보기는 했는데 700점은 안 나오더라ㅠ 그래서 결국 수료를 하게 되었다. 아마 졸업은 내가 토익을 딴다는 가정하에 21년 8월 졸업을 할 것 같다. 그때까지 코로나가 잠잠해져서 그때는 졸업식을 갈 수 있기를!!
그래서 올해 친한 동기들과 졸업 스냅사진을 찍었는데 너무 좋았던 기억이다 :) 내 대학 4년을 버티게 해 준 우리 컴공댕이들 고맙다
마무리
20년 회고는 19년 회고에 비해 어두운 내용도 있었다. 19년도는 마냥 밝기만 하고 감사만 했던 회고였던 것 같은데, 올해는 마냥 좋아 보이는 말만 쓰고 싶지 않았기 때문이다.
이제 진짜 신입 개발자로 21년을 시작할 예정이다. 항상 감사한 프루님 은님이 하신 말이 기억에 남는다. 3년 힘들게 공부하라고. 내년에는 더 더 책 읽어야지 진짜... 지금 당장 부족하다고 느끼는 것만 해도 디자인 패턴, 네트워크, 스레드, 디비 어우 너무 많다.
파트 내 시니어 개발자 분이 본인은 신입 때 한 달에 두 권씩 책을 읽었다는 말을 하셨는데, 그렇게 공부를 하셨으니 지금 이런 퍼포먼스를 내시는 거였구나 싶다. 지금 주변에 너무 대단한 분들이 많은 환경이니 나도 우리 메시징에 적응하기 위해서는 따라가야지 +_+. 꾸준히 공부하는 게 내년 목표이다.
파이팅 :)
ps. 내년 회고는 꼭 31일 말고 30일에 써야지..
+ (21년 12시 지나서) 올해 카톡 안터졌다..b 파트분들 넘 멋짐쓰
Last year I also wrote my year-end reflection on the 31st, and here I am writing it on the 31st again this year...lol No, actually I was going to write a post about job hunting before this, but due to time constraints, let's save that for the new year haha... Wow, and I'll be 24 in '21, time flies...
This year there's been so much talk about how COVID has made job hunting harder, and thanks to that, I feel like I experienced every possible emotion through my job search this year..lol This year's reflection is going to read mostly like a job seeker's diary, but let's get started anyway hehe (hey, at least I'm writing it, right..)
Wow.. looking back at this now, I was really full of passion back then.. It's kind of like a one-year diary when you write a year-end reflection, but it brings back so many memories
[January - February] Internship Applications and Various Activities
Internship Applications and Acceptance
It wasn't intentional, but I sort of had my own college roadmap lol — Freshman year: on-campus dev club / Sophomore year: off-campus dev club / Junior year: company-partnered extracurricular dev activities / Senior year: company internships and job searching. That's how my college life went.
So I'd been preparing for internships since December 2019, and I remember applying through various channels — professor recommendations, referrals from people I knew, direct applications, and more. As a result, most of the document screening and coding test results came out around January–February.
February, when all the results came pouring in. Wow..
Back in January–February of 2020, I genuinely had no intention of getting a full-time conversion — I was just trying to build my resume with an experiential internship, so I'd done zero preparation for coding tests or interviews. But thankfully, they saw something good in me during the interview (wow, thinking back, I didn't prepare for the interview at all and crammed the night before for my very first intern interview.. the audacity..) and I got to intern at Naver in the first half of 2020.
So since I hadn't started yet, February should've been a time to rest and have fun.. but of course, with my unstoppable drive, I ended up making a mistake...
Backend Freelance Project
Yep... I was working on a freelance project due at the end of February..lol A guy I'd gone to Open Hack + a hackathon in China with asked me to turn a Java library into an API, so I took on the project. The main tasks were GCS integration + external library integration, and wow.. that's when I really felt the importance of test code.
Honestly, I thought I could just whip up a simple controller in Spring and plug the Java library into the service layer, so I ignored object relationships and clean code and just wrote spaghetti code however I wanted.. But then all these errors I couldn't understand started popping up. I finally came to my senses, wrote test code, refactored the code piece by piece, and restructured things internally so each object had a single responsibility. That's when I truly felt the dangers of writing code that ignores maintainability and object-oriented principles.
So... I actually remember being so busy right before my internship interview fixing this freelance project code, then going to the interview, and continuing to work on it right up until my start date. As far as I know, they're not using the server I built anymore, but when I looked it up while writing this reflection, it shows up in search results.. ooh, looks like they're doing great as a student startup.
Naver AI Burning Day
And.. right.. I went to Naver AI Burning Day.. I literally said "never doing another hackathon!!" after the China hackathon in my junior year. But apparently it's like an annual event for me. (And I said the same thing when Burning Day ended too — "no more hackathons -_-"..lol)
Since it was an AI-focused competition, I thought there wouldn't be much for me to do, but ahhh... every hackathon I go to, there's always something new I struggle with. If I remember correctly, the issue was that it was hard to debug when running the Python code written by our team's AI engineer in Spring, but I don't remember the details.
Also, they provided Naver Cloud Platform credits for this event, and I had a hard time getting used to the platform (but thanks to that experience, I actually have a server running on NCP these days).
All black — the quintessential developer look lol
The amazing thing about hackathons is that somehow you always manage to finish something..(And I kept saying I'd never use Spring again at hackathons where productivity matters... but I always end up using Spring anyway)This event actually had a networking component where you could talk with Naver employees and potentially get job opportunities depending on the person, but since I'd already been accepted for the internship, I just happily collected the swag >< (The sweatshirt I got is great for stealth-wearing to work, so I wear it all the time)
And during the recreation time, I somehow won AirPods as a prize... nice... But since I already had a pair, I sold them on Danggeun Market lol
It wasn't an event I poured my heart and soul into, but looking back, I did a lot between January and February..haha That's when I realized I'm really bad at algorithms... I totally got carried by my team (nostalgic..) and I'm still not confident about algorithms ㅠㅠ
Ewha Dongsan Committers
As I also wrote in my previous DSC Ewha Lead reflection, around that time a few DSC members and I created a daily commit group. We used Slack and a web app as tools to track our commit records, and for the web app, we forked the code from a group that had been doing something similar and customized it for our group. (Related post: jyami.tistory.com/123?category=865823)
The existing code was written in Django and wrapped in Docker, and I really struggled with the environment setup. I wasn't familiar with Django or Docker at the time, but hmm.. something I've been feeling lately is that the more I go on, the less it matters what framework or tool you're using. Maybe it's because I've gotten better at Googling lol. I've developed the mindset that if I don't know something, I just search for it, deploy it, build it, and muddle through.
[ March - June ] COVID and Senior Year First Semester with First Internship
The COVID Boom
In March 2020, COVID cases surged rapidly. As cases increased, many activities had to be suspended — most notably, all university classes went online (which meant I could barely do any DSC activities..), and the company switched to remote work too. I had carefully arranged my school schedule so I could manage both in-person classes and work, and was waiting for my start date, but then the start date got pushed back and I ended up onboarding from home. (And this pattern of COVID getting worse and going back to remote work right around when I start would repeat itself later...)
And above all else — I'd found a new place to live in anticipation of the internship, but since we were working remotely, it was more comfortable staying at my parents' house, so the rent felt like such a waste... ㅠㅠ
First Internship
I wrote about my first internship in great detail in the post below lol and so many people read it, so I'll just link to it here.
Senior Year First Semester.. Graduation Project Done!
Even though I'd set things up to juggle school and work, there were still things to do. I had to finish the graduation project I'd already been working on in order to graduate, and I was so grateful to my friends during this time... Since I could only work on it after getting off work, I couldn't code much or participate as actively, but we wrapped it up well thanks to them...heh I think that was honestly my first time free-riding in my entire college life...lol
Because of this graduation project, I was coding after work, deploying, even writing Android XML — I did all sorts of things lol But I learned that balancing work and school is really, really hard ㅠㅠ I wanted to just focus on work but couldn't, and that made me sad.. But if I didn't do this, I couldn't graduate, which meant I couldn't get a job lol (But of course, I'd end up repeating this same mistake later..)
In the end, my senior first semester — out of 14 total credits, 11 were pass/fail lolololol omg lol (Pro tip for balancing work and school: pass/fail) And I finished my graduation project with an A. I had originally considered graduating early, but I was worried that taking more credits for early graduation would strain my internship, so I decided to postpone it.
[ June - August ] A Brief Job Search and Sweet, Sweet Rest
Overcoming the Blues
Right after my conversion interview in mid-June, I had a bad feeling about it lol so I actually updated my resume that very same day lol I even wrote an English CV that I'd never had before and added my internship experience.
Beyond just updating my resume, I applied to multiple places and tried my best not to fall into a depressive funk. At first, thinking about how I'd failed at something I'd hoped for and worked toward for so long, I cried a lot and had a really hard time. But I recovered quickly thanks to an unexpected reason.
The sense of loss from being rejected actually fueled my fighting spirit lol I started studying consistently again and sent my resume everywhere. And as I was applying, I suddenly realized — oh, this means I now have the opportunity to aim for places I want even more. In fact, out of the 4 places I applied to, I ended up getting final offers from 3 of them, and the confidence that many places saw my value helped me overcome everything.
I had my Naver internship conversion interview in mid-June.. I'm still amazed at my own ability to take action
So I think I studied for job prep for about a month and a half. Every day I practiced algorithms using the daily LeetCode challenge, and for about 5–6 interviews, I reviewed the tech specs of projects I'd worked on and double-checked my computer science knowledge each time. This period is really memorable to me too. If I had just gotten the job, I probably wouldn't have done even the bare minimum of CS studying needed for employment, so I'm glad I had this opportunity to study. It helped me organize everything I'd learned over 4 years of college, and the foundation I built during this time served as an outline for the fundamentals I'd later need to study more deeply, so I'm glad I did it.
It was a short period of job searching, but it was a time when I could get myself together both technically and mentally. Actually, this wasn't the first time I'd been knocked down by something big and bounced back. When I was applying to college, I didn't get into the school I'd prepared for over a long time, but it turned out to be a blessing in disguise — I ended up going to Ewha andgot even bigger opportunities. Looking back, I started to think — maybe it's like a jinx (or pattern) that's happening again now too?
Kakao's Long, Long Process
I was actually writing a separate post about this part, but I couldn't finish it so I ended up writing the year-end reflection first..ㅠ Once I finish that post, I'll link it back here too hehe
While job searching, I had absolutely zero intention of applying through public recruitment. I figured that public recruitment would involve way more quantitative evaluation factors rather than customized questions about each individual's activities. I had doubts about whether that kind of evaluation could truly see my value, and I was too impatient to wait for the various IT companies' public recruitment announcements to come out.
Of the 4 places I applied to, the one I wanted to go to the most was Kakao. Actually, the other two places that gave me final offers were faster with their timelines, but since this was the place I wanted to go to the most, I declined to sign contracts with both of those companies and focused solely on my interviews here.
Looking at it now, the Kakao hiring process had SO!!!! many steps
I applied through the year-round recruitment to the Platform Services Development team. For new grads, applying through year-round recruitment came with the penalty of working as a contract employee for 3 months and then being evaluated again, but I wanted to go so badly that I was willing to ignore all of that..!!!!
The process was as above — the phone interview was 1 hour, the 1st interview was 2 hours, and the 2nd interview was 1 hour.. I got accepted through a total of 4 hours of interviews. Thinking about it now, the people who interviewed me are the ones I work with, and I'm so grateful for all the consideration they showed.
One of the reasons I wanted to go to Kakao was actually the vibe of the interviewers. I believe that an interview isn't just a place where interviewers evaluate the candidate — it's also where the candidate evaluates the company. And Kakao's interviewers asked the deepest questions about computer science knowledge and about my activities, which left a great impression. Even for a single technology, they asked what I considered when using it and whether I had a theoretically correct understanding of the technologies I used — most of the questions were like that. I got the feeling that they asked such deep questions because it was a team where you needed to study and know more. And when I answered a question incorrectly, they'd say in the next interview, "Would you like to try answering this again?" — giving me a chance to improve, which I really appreciated.
All interviews were conducted via video call, and since they were all virtual, I never got to visit the Kakao office that I'd always wanted to see.. sob sob.. (But it's okay because I got accepted and visited after joining heh) Still, as a job seeker in the COVID era, I think meeting face-to-face for interviews would actually feel more awkward going forward. I've gotten so used to video interviews.
There are so many more impressions I had, but I'll save those for the job hunting post I'll write later lol
Acceptance and Sweet Rest — Solo Jeju Island Trip
After finally getting accepted to the place I wanted most, I took some time to refresh that I hadn't been able to before. I'd been running nonstop and was burned out, so I really wanted some rest. Even when I was in school, I never took a leave of absence and spent my breaks on extracurricular activities. The reason was... even when I tried to rest, I knew I'd end up doing something anyway, so I never bothered with taking time off lol
But even in August, meeting people was uncomfortable because of COVID. I'd been planning a Europe trip on my bucket list — I was going to go during the most stress-free time possible, the month before starting my job after securing employment. But COVID...ㅠ Going abroad and catching COVID would only cause problems for my start date, so I gave up. Instead, I took precautions and went on a solo domestic trip that I'd always wanted to try.
I went to Jeju alone, ate samgyeopsal by myself, learned to surf, and wandered around a lot to heal. The Jeju ocean is always beautiful no matter when you see it.
It happened to be typhoon season, so I got to experience a typhoon full-force. I had planned to stay in the hotel all day because of the typhoon, so I'd even brought my laptop to play League of Legends all day!!! But then the hotel lost power because of the typhoon lololol sigh... In the end, things didn't go as planned ㅠ
It was a bit tough since I don't have a driver's license and had to walk everywhere, but it had its own charm. A woman I'd just met for the first time drove me back to my hotel because it was dangerous with the typhoon coming, and while it was tough walking everywhere, the fact that I was on foot made for some spectacular experiences.
Other than Jeju, all I did before starting my job was play games lolololol I'd always liked League of Legends, but during this period I made gaming friends to play with, so I was literally going to bed at 6 AM and waking up at 3 PM... living the gamer life on repeat heh I would've written about my rank in this reflection, but unfortunately since we always played normals in a 5-man queue, only my total game count on op.gg kept going up(sigh... I should've been preparing for graduation during this time...)
Angel Hack Participation
Oh, now that I think about it, I also participated in AngelHack while job hunting. AngelHack was held remotely this year, and I joined as a team of five with Sohyeon and Wonjong who I'd done the China hackathon with + Hyeonseok who I'd been on the filming team with at LikeLion + Minhyung who I was doing a Java Spring study group with. (I got a little exposure to Spring Security during this, and I honestly still don't get it lol)
I'd been saying "I'm absolutely never doing another hackathon!!" back during the Naver Burning Day, but since these are all people I've known for a long time, I guess the pull of doing a hackathon with close friends was too strong haha.. I ended up doing it again. When are we going to finish this project though... The more hackathons you do, the more unfinished code piles up, and that makes me sad sigh..
We started working online and then for the last 2 days, we all gathered at an Airbnb to work together. We filmed timelapses, nobody could sleep lol — every hackathon, I always feel like the frontend people have it the hardest. They always seem so busy.
One of the fun things about AngelHack was that since it was online, the organizers prepared online activities too. They gave out prizes based on online game scores, and I got obsessed with the Chrome dinosaur game...(I go crazy for any game, apparently) I wasn't even developing — I just kept making the dinosaur jump.
[ September - December ] Joining Kakao and Graduating?
Life at Kakao
While I was just living my life playing games, my start date was approaching. But why does COVID always get worse right when I'm about to start a new job, like I mentioned earlier ㅠ I ended up doing my interview remotely and onboarding remotely too. The workbook and all the goodies❤️ I got during Kakao's remote onboarding program were so nice hehe Oh, and I also found out which team I'd be working on, which I didn't know during the interview — I was assigned to the Talk Messaging part, which handles the chat functionality of KakaoTalk. It's the most core service, and I realized from day one that there was so much to learn and understand, but now I feel it even more and more and more..ㅠ Please, let me study
Goodies from onboarding
I worked fully remote for about a month. Then about a month later, COVID eased up a bit, so I did rotating office days for about two weeks, followed by full in-office work for about two weeks. (Then it got bad again and daily cases hit the 1,000s like we're seeing these days.)
Honestly, it was tough to adjust when I was only working from home. Asking questions was always something I agonized over. I kept thinking, "Am I asking something too easy? My mentor is busy with actual work — am I being a bother..?" But whenever I did ask, they always explained things so kindly. And I was so happy to have met someone who is genuinely the most amazing person I've ever encountered — my mentor probably doesn't know this, but I truly admire them lolol Hahhh... truly incredible... I want to be like that someday
Anyway, it really does seem like new hires benefit from going into the office. When I was in the office, I gradually got used to the daily life with my team members and started feeling a sense of belonging. So I actually wanted to go to the office more than anyone... (I really wanted to go in during the year-end response too..) Unfortunately, things are just too bad right now...
My WFH setup (left side is blurred because it's company code)
As a new hire through Kakao's rolling recruitment, I was on a contract position and similarly received an intern project to work on. Unlike Naver, the project was closely tied to the team's work so I can't go into detail, but I got to try a lot of new challenges. And I also did a lot of self-reflection.
What was new this time was working with Netty programming + Kotlin. I had wondered if it was too early to study Layer 4 networking, but through this opportunity I got to read books and look at actual production code to learn — though I'm still confused ㅠ Being on this team, I really feel like there's so much to study. I understood why during the interview they had asked me intensively about threads, asynchronous programming, and synchronization — concepts that were quite difficult. Seeing the theoretical answers I had worked so hard on actually being used in code with things like threadExecutor and synchronized, I wondered, "Am I really allowed to touch this stuff?"
As for my first impression of Kotlin — wow, it really is convenient!!! But since I'm still less familiar with it than Java, next year I plan to read "Kotlin in Action" to avoid anti-patterns and study how to use Kotlin the Kotlin way — things like sealed classes, suspend functions, and coroutines. Personally, what I really liked was how convenient the constructors are. I considered building my project server with Kotlin + Spring too, but since evaluations were on the line, I went with Java + Spring since I'm more comfortable with it — and the Kotlin ⇔ Java interop is really well done. I definitely need to try the Kotlin + Spring combo! +_+
And once again, I deployed on k8s this time too. Previously I used Helm to manage objects, but this time the team was using Kustomize, so I gave writing Kustomize configs a shot. My personal take is, hmm, I'm not sure if this analogy is perfect, but Helm feels like binding data to a JSP template — you inject values into a given template — while Kustomize feels like Java overriding — you set up common base configurations for objects and then only override the parts that need changes based on your settings. But honestly, any tool works fine as long as you use it well for its intended purpose +_+ This time I also configured in-cluster networking when using k8s, which made me feel like I leveled up.
A Bit of a Slump, But Passing the Full-Time Conversion
My name tag and employee badge!! Yay
Nobody probably knew, but I actually went through a slump. I had essentially spent the entire year on internships, and being an intern meant I had limited access and was given projects somewhat removed from actual production work — so I felt disillusioned about writing code all by myself while only peeking at real-world work. Code that only I commit and merge is bound to stagnate and has its limits, and doing solo projects continuously was draining.
If I had come in through an official intern recruitment cycle like other friends and had fellow interns to go through it with, maybe I would've had more people to lean on. If I had prepared for the regular hiring process like many other developers, I would've had colleagues who joined at the same time and wouldn't have had the instability of being on a contract — maybe I would've been a little less anxious. I kept thinking about these "what ifs" and feeling regretful. Normally I'm the type who doesn't regret my decisions and believes I made the right choice at the time, but even though I knew that rolling recruitment + interning was the best option for me and that I was doing well enough, I just kept feeling down.
Usually when I feel down, I'm the type to meet people, recharge, and motivate myself again, but COVID made that impossible, which made it even sadder. So I ended up spending more time on Discord with my gaming friends since I could always chat with them, which led to a vicious cycle of spending less time studying development and more time gaming.
So right before the full-time conversion interview, during my most mentally unstable period, I went back to my parents' house and deliberately spent time with family. I hung in there, stayed focused during work hours, and fortunately I must have made a good impression in the interview because I passed the conversion! I was so happy about passing that I told everyone and got so many congratulations — that's when I escaped the slump hehe. I think the instability around my job and sense of belonging was the biggest cause of my slump.
Starting YouTube
Yep.. I started YouTube. The beginning was actually really casual. I posted a time-lapse of myself working from home on my Instagram story, and my friends were like "YouTube vibes?! Go for it~!" so I was like hmm.. should I actually try?! And I boldly started in September lolol
But as the conversion interview got closer and I got busier, my upload frequency slowed down in November and December — but ohh,,, amazingly, a lot of people have been showing love (888 subscribers as of now!!). Looking at the link thumbnail above.. I just threw together the thumbnail and background, so I need to change it to something more YouTube-appropriate, hehe. But it's not important so let's procrastinate on that. Personally, what I liked about filming YouTube was that when I film "code with me" or "study with me" videos, I'm sort of forced to study lolol Nice... b
+ (January 1, 2020) A junior drew some art for me a while back, so I changed my profile picture >.0
Challenge Semester Project for Graduation
This retrospective has a lot of foreshadowing, it turns out. I mentioned how tough it is to do school projects while working at a company, and sure enough, during this contract period I was also doing a school project lolol The reason things turned out this way is... well, COVID.
When I applied for the challenge semester, I had 6 credits of major courses left, and I thought COVID would calm down in the second half of the year and school would go back to in-person classes. So I figured if I was juggling work and school, I'd never be able to attend in-person classes, and that's why I chose the challenge semester system — where you set up your own project and complete it within the semester. (So after work I'd be developing this..ㅠ So exhausting... I should've done this during all of August instead of playing League lol) But what I regretted was that if I'd known COVID would get this bad, classes would've been online anyway, so I could've just taken regular courses and passed as long as I didn't fail, right? lol. That way I could've focused more on work!
The challenge semester project I chose was an expanded version of the Ewha Dongsan Committers I mentioned above — I built a developer community for Ewha students. I did both backend and frontend development with Spring and React, and deployed on Heroku, but the server is currently down because I found some bugs.
Currently, the commit history fetching work is implemented as an API even though it should be a batch job, so it needs to be fixed. I also found an error in the error handling part due to my lack of familiarity with Spring Security right before deployment. And the frontend was, well... coded in a very hackathon-style way that I'm not happy with, so I didn't deploy it for actual users. I've only gotten beta version feedback from people around me, and hmm.. I hope I can fix it up and actually deploy it by February next year lolol
Dashboard screenshots (originally viewed by scrolling vertically..)
This project earned me 6 major credits, and I was able to fulfill all 140 credits required for graduation. So my final university GPA ended up at 3.85 on a 4.3 scale / 4.03 on a 4.5 scale. Since I basically took pass/fail courses for both semesters of my senior year because of work, my GPA was essentially finalized during junior year.
But there was one more obstacle left for me...
Ended Up Just Completing Coursework...lol Graduation Is Next Year lolol Let's at Least Take Some Photos
Yep... I had considered early graduation, but I couldn't even graduate on time and ended up just completing my coursework without officially graduating. The reason is TOEIC lol. My school's CS department graduation requirements include scoring over 700 on the TOEIC. But haha.. I always pushed English to the very last priority, so it kept getting pushed behind work + various school projects + dev studying, and I never studied for the TOEIC.
I took the TOEIC once without studying at all, hoping for the best, but I didn't hit 700 ㅠ So I ended up just completing coursework without graduating. Assuming I pass the TOEIC, I'll probably officially graduate in August 2021. I hope COVID calms down by then so I can actually attend the graduation ceremony!!
So this year I took graduation snap photos with my close classmates, and it was such a great memory :) Thank you to my CS crew who got me through 4 years of college
Wrapping Up
My 2020 retrospective had some darker parts compared to my 2019 one. The 2019 retro was nothing but bright and grateful, but this year I didn't want to only write things that looked rosy.
Now I'm about to start 2021 as a real junior developer. Something that Pru-nim and Eun-nim — for whom I'm always grateful — said sticks with me: "Study hard for three years." Next year I really need to read more books... Just the things I feel lacking in right now — design patterns, networking, threads, databases — ugh, there's so much.
A senior developer on my team mentioned that when they were a junior, they read two books a month — and I thought, no wonder they perform at this level now with that kind of studying. I'm surrounded by so many incredible people right now, so I need to keep up to adapt to our messaging team +_+. Studying consistently is my goal for next year.
Fighting :)
ps. Next year's retrospective — I'll definitely write it on the 30th, not the 31st..
+ (Past midnight into '21) KakaoTalk didn't crash this year..b Our team is so cool
우연히 지난 스마일게이트 윈터캠프때의 인연으로 알게된 오빠가 DSC라는 프로그램을 소개해주면서 알게되었다. 근데 마감일이 당일이었는데 어떻게 호다닥 써서 준비했던게 합격을 하게되었다.
합격까지의 과정은 1. 서류제출 2. 행아웃 인터뷰 이렇게 두개였는데, 지금의 절차는 하나 더 추가되었다고 한다.
1. 서류제출
문항은 리더십/입팩트와 개발 경험에 대한 질문으로 이루어져있었다.
리더십 질문
본인을 소개할 수 있는 영상을 유튜브에 업로드
개발 프로젝트에서 리더를 한 경험에 대한 질문
Google Community program 참여 경험에 대한 질문
발표 경험에 대한 질문 : 대상자, 주제, 이벤트, 인원수
누군가를 가르친 트레이닝 경험에 대한 질문 : 대상자, 주제, 인원수
리더십 질문으로는 아래와 같았는데, 3번을 제외하고 모든 문항을 작성했었다. (당시에는 GDG의 존재도 몰랐었다.) 문항이 너무 많았고, 글자수 제한도 없다보니 모든 문항을 잘 작성하려 하기보다는 내가 한 경험으로 내 장점을 어필하면서 자세히 풀어서 썼다.
이번 리드지원, 혹은 여러 통로를 이용해서 나한테 조언을 얻고자 하는 사람들에게 항상 하는 말인데, 내가 단점이 되는 부분은 굳이 먼저 쓸 필요가 없다고 생각한다. Why Not? 그게 단점이야? 라는 태도로 주구 장창 나는 장점만 말하는 편이다ㅋㅋㅋ
그래서 내가 구글 커뮤니티와 연관이 많다는 점 보다는 나는 이렇게 사람들앞에서 말도 해보고, 누군가를 가르치는 것도 플젝 경험도 많은사람인데 이 활동은 분명히 나랑 핏이 맞을꺼야라는 뉘양스로 자신있게 지원서를 작성했다.
참고로 1번에서 만든 내 소개 영상은 내 유튜브 채널에 비공개 영상으로 저장되어있다ㅋㅋㅋㅋ : 아무에게도 보여주지 않을테야... 당일에 지원서 작성 + 영상 업로드 다 해야했다보니 편집 하나도 없이 그냥 캠켜서 찍은 영상이었는데 지금보면 쪽팔리다...ㅋㅋㅋㅋ 지금 하라고 하면 옛날에 했던 멋사 영상팀 경력을 살려서 짐벨도 대여하고 카메라 워킹 시나리오도 짜서 인생 영상으로 한번 만들어보고싶다..
개발 경험 질문
github 프로필 링크
안드로이드 앱 개발 경험과 프로젝트 설명
웹 개발 경험과 프로젝트 설명
클라우드 개발 경험과 프로젝트 설명
머신러닝 개발 경험과 프로젝트 설명
개발 경험도 마찬가지로 내가 모르는 부분은 진솔하게 모른다 쓰고 내가 잘하는 부분만 잘 녹여내서 썼었다. 그때 클라우드 경험으로 AWS이야기를 했는데ㅋㅋㅋ 이런..ㅎㅎ 아무래도 DSC 활동을 하면 관련 기술에 대한 뉴스레터 수신, 각종 기술에 대한 학습 영상을 제공받게 되는데, 어느 분야에 대한 관심이 있는지를 보려고 넣은 질문인 것 같다.
당시에는 개발 능력이 엄청 중요하지 않을까라고 생각해서 개발 능력만 엄청 어필해서 썼던 기억이 난다. 근데 1년이 지나고난 지금 실제 활동은 리더십이 훨씬 중요했다. 그래서 지금 다시 리드 지원을 한다고 하면 리더십 부분에 좀 더 초점을 맞춰서 지원서를 작성할 것 같다. 개발 경험의 경우엔 어차피 깃허브에 선호하는 언어, 프레임워크에 대한 정보가 나와있기 때문에 그사람이 개발을 어떻게 대하는지에 대한 태도가 있기때문이다.
2. 행아웃 인터뷰
Google Korea DevRel 팀과의 1:1 인터뷰를 진행한다. 당시 꿈에 부풀어서 이것도 하고싶고 저것도 하고싶다고 열심히 말했던 기억이 아직도난다.
인터뷰는 주로 기술질문보다는 리더십 + 본인의 경험에 대한 질문을 물어봤었다. 1년이 지나서 기억은 잘 안나지만 당시 내가 어떤걸 컨트리 뷰션을 하고싶냐는 말에 대답했던 뉘양스는 어렴풋하게 기억이난다.
저는 컴퓨터공학과 소속인데, 저는 외부 활동을 통해 여러 프로젝트를 하면서 내가 어떤걸 좋아하는구나를 알게되었고, 계속해서 공부를 해왔다. 근데 주변에 많은 친구들을 보면 컴퓨터공학과임에도 불구하고 학교 수업에만 열중하다보니 경험이 없어 본인이 어떤 분야를 좋아하는지를 모르는 경우가 많다. 나는 학생들이 스스로가 직접 만들어보는 경험이 중요하다 생각하고 그 경험들이 모여서 자신의 진로를 고민할 수 있는 기회가 많아진다고 생각한다. 그래서 우리학교 학생들에게 DSC가 좀 더 많은 개발 경험을 접할 수 있는 기회가 될거라고 확신한다.
그래서 나는 내 DSC 활동에 멤버들이 1년동안 공을 들여서 본인의 프로젝트를 완성하는 경험을 할 수 있도록 활동을 했었다.
외에도 블로깅에 대해서 여쭤보셨는데, 내가 하는 활동을 블로그를 통해 전파하고싶다는 말씀을 드렸고 실제로 우리 활동에서는 팀블로그를 이용을 했으며, 내 블로그 곳곳에 DSC의 향기가 있다ㅋㅋㅋ 와.. 그때만해도 블로그에 글이 3개인가 그랬는데..ㅋㅋ 1년이 지난 지금 발전한 모습이라서 다행이다!! 후후
3. 합격 메일
면접을 마치고 먼저 Google Korea에서 연락이 온 후에 합격 메일을 공식적으로 받게되었다 :) 당시 영어로 메일 받았다고 엄청 신기해 하면서 하나하나 번역하면서 꼼꼼히 읽었었는데ㅎㅎ 이젠 영어메일 쯤이야!!! 아직도 가뿐하지 않다
4. 2020 리드 지원때 온 연락
이번 리드 지원때 여러 질문을 받았었는데, 한 분이 기억에 남는다. 당시 나의 개발 수준과 상황을 여쭤보는 질문이었는데, 프로젝트 경험 깃허브 프로필 등 어느정도로 관리해야하는지를 여쭤보셨다.
여기에 대해서는 리드마다 모두 스펙이 다르고 모두 좋아하는 분야가 다르기 때문에 모두가 같은 포폴을 가질 수 없고 자신이 하고싶은 일들을 어필하는게 중요할 것 같다는 답변을 드렸다. 실제로 우리 리드들 같은 경우도 비전공자도 있었고 서로 관심사도 모두 달랐다. 내가 백엔드 몰빵으로 관심이 있었다면, 정동님은 처음에는 ML에 관심있다가 DSC 활동 후에 Cloud 몰빵 캐가 되었다.
리드 활동 같은 경우는 일단 누군가에게 기술 교육을 받는 활동이 아니고, 본인이 개발 관련 활동을 만들어 나가야 한다. 그래서 리드 지원할 때 어필해야하는 점은 본인이 이 활동으로 어떤 사람들을 만나고, 어떻게 컨트리뷰션을 하고 싶다라는 계획성이 보이는게 중요하다.
어찌됐든 리드활동은 누군가가 시켜서하는게 하는 것보단 스스로 나서서 해야하는 일이기 때문에, 내가 공부하는 기술의 대단함보다는 나의 주체성을 어필하는 것이 더 도움이 된다고 생각한다. 사실 그 기술의 대단함은 이미 Googler가 갖고있는 것이니 대항생인 우리들은 우리의 열정을 보여주자!!🔥🔥
첫 시작 : Google Korea Onboarding
나의 가장 첫 활동은 온보딩 프로그램이었다. 지금은 코로나 때문에 온보딩도 행아웃으로 진행한다고 한다. 그치만 당시에는 구글 코리아 오피스에 가서 DSC 활동에 대한 정확한 소개를 받고, 여러 지역에 있는 리드들과의 만남을 할 수 있었다. 아래 엄청 자세하게 써두어서 링크를 첨부한다
아무래도 내가 활동한 기수가 1기이다 보니, 각종 DSC Korea 페이스북, 슬랙, 카카오톡을 파면서 하나의 커뮤니티의 시작을 다들 준비해갔다. 아무런 체계 없이 백지에서 모든걸 창조해야하다보니 초반에 다들 힘들어했었고, 본인이 잘 하고있는건지 어떤걸 해야하는지 대한 고민이 많았었다.
그래서 온보딩 이후로 1~2달 후인가 우리 리드들끼리 2주에 한번 행아웃 화상회의를 통해 각 학교에서 어떻게 활동하는 중인지 공유하는 시간을 가졌다. 그래서 상대 학교에서 반응이 좋았던 행사는 우리 학교에서도 도입하고 그랬었다.
여러분 보고시퍼요오오ㅠㅜㅠㅠ
개강과 함께 DSC Ewha 시작
혼자 모든걸 한다는게 쉬운 일이 아니었다. 포스터 제작 (당시 중국해커톤을 하고있었는데, 그때 시상식때 나는 DSC Ewha 포스터 제작하고 있었다), 홍보 문구 작성, 커리큘럼 구체화, 면접, 선발 모든 과정을 혼자 했었다.
나는 모든 멤버를 선발한 후에 그 멤버들 안에서 코어 멤버(운영진 멤버)를 뽑았었는데, 지금 생각하면 먼저 코어 멤버를 뽑고 같이 어떤 활동을 할지 의논해 나가는게 더 좋았겠다라는 생각이 든다.
당시 준비했던 홍보 포스터와 문구
면접의 경우엔 개인 지원서에 특화하여 질문을 했었다. 어느 조직이든 마찬가지이지만 나는 면접은 이사람이 왜 우리 조직에 들어오고 싶은지에 대한 이유가 가장 중요하다 본다.
여러 개발 동아리가 있는데, 굳이 DSC Ewha를 해야하는 이유?
프로젝트 경험을 원하는 거라면 DSC가 아니어도 되는데 왜 굳이?
1년 후 기대하는 본인의 모습
github 관리여부
위 네개 질문을 주로 물어봤었다. 잘못한 일중에 하나가 지원자 22명중 22명을 전부 뽑았었다. 당시 생각은 본인이 지원하겠다고 한 일에 책임을 느낄 것이라 생각했으며 내가 생각한 선발 인원 수와 맞아 떨어졌기 때문이다. 하지만 이때의 판단은 후반부 이탈률이 높아진 것과 인과를 가진 것 같아 후회하는 일 중 하나이다.
DSC Ewha 멤버들과의 1년
사실 대부분의 DSC 활동이 19년도에 몰려있다. 코로나로 인해 20년도 활동이 애매모호해졌고 다들 온라인으로 하다보니 동기부여가 잘 안되는 상황이었다. 만약 20년도에 코로나가 아니었다면.. 좀 더 많은 활동을 멤버들과 할 수 있었을까 하는 아쉬움이 있다.
아무래도 대학이다보니 동아리 형식으로 활동을 했었다.우리의 주된 활동은 세미나 + 1년 팀프로젝트였다. 19년 2학기에는 정기모임을 가졌고 이 정기모임에서는 팀별 시간 + 세미나 시간을 가졌다. 반면 코로나가 시작되고 나서는 팀별 시간은 팀 자율에 맞춰서, 세미나는 팀블로그에 업로드 하는 방식으로 변경하였다.
1. 팀 프로젝트
이 DSC 활동이 내가 주도해서 하나의 강의를 하는 형식이 되기를 원치 않았다. 내가 SpringBoot 강의를 한다면 그만큼 멤버들이 체계적으로 공통적인 기술 하나에 집중한다는 장점이 있을 수 있지만, 후에 프로젝트를 할 때 여러 기술을 사용해 보지 못할 것이라고 생각했다. 그리고 애초에 그런 방식이라면 웹 개발 동아리를 하나 만들지 왜 굳이 DSC를 하겠는가.그래서 나는 멤버들이 자유롭게 원하는 기술을 이용해서 프로젝트를 진행하도록 했다.
나는 당시 운영만으로도 힘들어서 나는 팀프로젝트에 참여하지 않겠다 했는데 옳은 선택이었다. 3학년 1학기 때 대외활동을 전부 관뒀음에도 불구하고 3-2에 졸업프로젝트 + DSC Ewha 활동 + 스터디 자바봄 + 학업의 문제로 프로젝트를 할만한 여력이 안되었기 때문이다. 그리고 4-1에 네이버 인턴십까지 했었으니 집중하기 정말 어려웠겠다는 생각이 든다.
팀 프로젝트는 모임 첫날 아이디어 스피칭 후 원하는 아이디어로 팀 구성을 하게 했다. 이후 매주 모임시간마다 팀별 회의를 진행하고, 각 팀의 진행상황은 팀블로그에 돌아가면서 작성하게 했었다. 그러고 각 분기에 한번씩 (3개월에 한번) 각 팀별로 발표를 진행했는데, 코로나로 인해 2학기에만 대면으로 발표하고 나머지 겨울방학 / 1학기 / 여름방학에는 모두 행아웃으로 진행할 수 밖에 없었다.
분기 발표때 봤던 ppt 내용들
나는 기술적으로 도움을 준 부분이 거의 없었다. 멤버들이 주체적으로 팀원들끼리 android, react 스터디를 해서 나온 결과물이 이 활동의 프로젝트들이었다. 분기발표 때 멤버들이 하는 발표를 보면서, 디자인부터 백엔드 설계, 프론트엔드 통신, 모델 학습 데이터 수집까지 차근차근 진행했던 모습을 보고 감탄했었다. 프로젝트 기획부터 완성까지의 과정을 통해 멤버들이 본인의 커리어에 도움이 되는 프로젝트가 되었으면 좋겠다.
총 4개 프로젝트가 완성의 단계까지 갔으며, 그 중 핸들랭 팀의 프로젝트는 DSC Solution Challenge에 제출했다. 좋은 결과가 있으면 좋았겠지만 아쉽게도 DSC Korea에서는 순천향대 팀이 수상하였다. 소스코드는 깃허브를(github.com/DSC-Ewha) 이용해 모아두고 다음 리드에게 인수인계를 완료했다.
2. 미니 세미나
개발에도 다양한 분야가 있다. 많은 학생들이 하는 고민이 본인이 어떤 걸 좋아하는지 모른다는 것이었다. 그 이유중에 하나는 일단 여러 기술 및 분야의 존재를 모른다. 학습 방식 및 툴 조차도 모른다. 따라서 기술의 존재만이라도 툴의 이름이라도 알고 넓고 얕게 지식을 접할 수 있는 경로가 무엇이 있을까라는 생각을 했다.
그래서 나는 멤버들이 서로 본인들이 주로 사용하는 기술 혹은, 위 팀프로젝트에서 사용한 기술에 대해 개인이 공부한 내용을 공유하는 시간을 만들었다. 팀 프로젝트가 제각기 다른 기술을 사용해서 진행이 되었고, 공통 관심사를 갖는 멤버들이 있다면 더 이야기 할 수 있는 수단이 될 것 같았기 때문이다.
그래서 매주 모임을 가질 때 멤버별로 돌아가면서 약 15분간 미니세미나를 진행했고, 처음에는 그 세미나에 대한 간단 QnA 형식으로 DSC Ewha 페이스북 페이지(www.facebook.com/DSCEwha) 게시글을 채워나갔다.
그러나 나중에 코로나로 인해 온라인으로 팀 프로젝트를 진행하고 모임을 진행하지 않게되면서 미니세미나도 온라인으로하게 되었다. 팀블로그에 영상을 찍어서 올리는 방식으로 변경했는데, 나도 처음 접하는 주제들이 많았다. 이 활동도 코로나로 무산되면 어쩌지 했었는데, 멤버들이 책임감을 갖고 다들 업로드 해주어서 마무리 할 수 있었다.
(좌) 모임 할 때 세미나 카드뉴스 (우) 온라인 세미나에서 블로그에 올린 영상
활동을 하면서 좋은 점만 있는건 아니었다. 대면으로 활동할 때는 매주 모임을 준비하고, 다른 DSC 행사 준비와도 병행을 해야하다보니 신경써야할 것들이 많았다. 그러다보니 3학년 2학기에 커뮤니티 활동으로 인해 개발공부를 많이 못했던 것이 아쉬웠다. 반면 온라인으로 활동할 때는 멤버들의 이탈도 발생했고, 프리라이딩이 일어나기도 했었는데 여기에서 내 능력에 되게 회의감을 느꼈었다. 이때 아무래도 온라인으로 팀플을 하다보니 리드로써 어느정도로 팀을 케어해주어야했는데라는 후회가 있다.
이렇게 부족한 리드임에도 불구하고 프로젝트, 세미나 모든 활동을 적극적으로 참가해준 멤버들이 있기에 활동을 잘 마무리하고 인수인계까지 잘 할 수 있었다.
와... 여기까지 10월에 쓰고 다시 마무리하려고 텍스트창을 킨게 12월이라니ㅋㅋㅋ 그래도 마무리 짓는거에 의의를 두자!!
DSC Ewha에서 진행했던 행사들
1. DSC Ewha Open Codelab 모.같.코
행사를 하게 된 계기
기초 SW 교육으로 코딩을 배워본 친구들은 많지만 그것은 결국 언어레벨이다.교양 수업에서 배운 프로그래밍 언어를 가지고 어떤 일들을 하는지 코딩에 관심있는 벗들에게 알려주기 위한 흥미위주의 코딩세션을 진행하였다.구글 코드랩이 튜토리얼식의 입문자를 위한 설명이 잘 되어있다고 판단하였기 때문에 이 행사를 기획하게 되었고, 이화여대 내 DSC Ewha의 입지도 키울 수 있는 큰 이벤트라고 생각했다.
당시 행사 진행 포스터와 FB에 올렸던 포스팅
행사 준비 과정
준비과정에서 DSC Ewha 멤버들이 튜터로 참여하므로써 혼자 모든걸 하지 않으려했었고, 다행이도 많은 친구들이 같이 해주었어서 우리도 재미있게 행사를 준비했던 기억이난다. 그리고 나는 리드로서 행사에 필요한 비용마련을 위해 이화여대 소프트웨어 중심대학 사업단쪽과 컨텍을 하는등.. 사실상 개발자의 역할보다는 관리자의 역할로서 총괄을 하였던 기억이 난다.
행사 진행
행사는 우리학교 기준 한교시동안 진행하였다. 총 1시간 15분 중 5분은 DSC Ewha를 설명하고 DSC Korea를 시간하는 OT시간, 60분은 튜터들과 코드랩을 같이 실습하는시간, 10분간은 설문조사를 요청드리는 시간으로 타임라인을 잡았다.
코드랩을 진행할 때는 메인튜터, 서브튜터로 나누었으며 메인튜터가 앞에서 코드랩을 라이브로 진행하고 서브튜터들은 실제 학생들이 모르는 부분이 생길때 그부분에 대한 추가 설명이나 도움을 줄수있도록 하였다.
행사는 총 2회 (1회에 세션 2개씩 총 4개 세션) 진행했으며 내 기억상 10월과 12월 두번이었다. 심지어 12월에는 여러 교수님들도 들어와서 참관했던 기억이...ㅎㅎ 행사진행후 설문조사에서의 의견도 전부 긍정적이었어서 다들 뿌듯해했던 기억이다. 원래는 올해 초 내가 리드를 할 때만해도 계속 주최를 할 예정이었지만 코로나로인해 다들 온라인에 적응을 못한상황이라 전부 취소하고 이후 계획을 세우지 못한 것이 아쉽다.
당시 긍정적이었던 피드백들..ㅎㅎ
2. 이화동산 커미터스
DSC Ewha 내부에서 진행했던 행사이다. 겨울방학 + 1학기 개강 일시에 걸쳐서 70일 동안 일일커밋 그룹을 진행하였다. 기존에 DSC HUFS에서 진행했던 활동방식을 참고했으며, GDG 판교에서 했던 행사인 잔디콘을 참고해서 구체적인 방향을 정했었다. 그렇게 우리 그룹은 총 9명이 진행했으며 70일 전부를 참여한 사람은 7명이었다.
매일매일 일일커밋이 일어나는 걸 서로 관제하기위해 slack의 github 플러그인을 이용해 각자의 레포지토리에 변경이 있으면 알림이오도록 했다. 물론 커밋을 하지 않았을때 벌금도 정했었다.
원래는 70일이 끝나는 마지막날인 3월 28일에 학교에서 이화잔디콘이라는 네이밍의 컨퍼런스를 열어 우리가 했던 일일커밋활동에 대한 기록을 불특정다수의 동문들에게 발표하려했으나 코로나로 인해 역시 무산되었던 점이 아쉬웠다.
당시 커밋을 관리하기 위한 툴들!
위와같은 관리수단으로 우리는 모두 1일 1커밋을 실천하려고 하는 커밋그룹을 진행했었고, 혼자서는 꾸준히 하기 힘들었을텐데, 이런식으로 같이 하다보니 나에게도 도움이 많이되었었다.
이화동산 커미터스를 마치고나서의 후기
이화에서 했던 활동 모두 내가 기획하고 관리해서 좋은 반응을 이끌어냈으나, 모두 N회 더 하고싶다는 욕심이있었는데 20년도 이래저래 정신없는 상황으로인해 주된 활동이 19년도에만 있었어서 아쉬운 점이 있다.
개인적으로 모같코를 진행할 때는 내가 공부하고 싶은 기술에 대한 깊이나 탐구보다는 동아리를 알리기위한 이벤트성으로 코딩에 흥미를 붙이기 위한 목적만 신경을 썼었다. 그러다보니 사실 기술적으로는 나에게 도움이되는 내용보다는 입문자를 위한 내용이 앞썼다. (아무래도 불특정 다수를 대상으로하는행사들은 입문자를 대상으로할 때 가장 많은 인원을 모을 수 있기 때문) 그러다보니 이 행사를 준비할 때는 나의 개발자 커리어적으로 기술적인 향상은 없을 수 밖에없었고, DSC Ewha를 할 때 행사 및 동아리를 준비 운영하는 리소스가 나의 기술적 전문성 향상을 위해 투자하는 리소스보다 훨씬 컷던게 아쉬움이 남았다.
하지만 커미터스 활동은 나또한 1일 1커밋을 진행하면서 꾸준히 개발공부를 하는 습관을 들이고, 실제로 개발공부를 통해 기술적으로 성장을 할 수 있는 시간을 오롯히 나만을 위해 마련했기 때문에 나도 만족도가 높았던 활동이다.
구글 커뮤니티와 연계하여 했던 나의 활동
DSC 활동을 하면서 Google Korea Developer Relation 팀과의 커뮤니케이션은 필수적이었다. DSC Korea는 2019년 처음 진행하기도 했고, DSC 외의 구글에서 지원하는 다른 커뮤니티와의 협업도있었기 때문이다. 그러다보니 자연스럽게 GDG, WTM, GDE 분들이 주최하는 행사도 참여하고 같이 협업하기도 했었다.
가장 먼저 GDG Seoul에서 진행했던 DevFest에 스태프로 참여하면서 어떤식으로 행사를 진행하는지를 살펴보고, 이때부터 내가 만들 DSC 행사들을 기획하고 생각하게 되었다. 자세한 후기는 위에 링크에 정리해두었다. ( 평소에 하나하나 잘 정리해뒀어야했는데..ㅎㅎ )
핸즈온 세션에 기대해서 개인적으로 갔다왔었다. 아무래도 리드로 활동하다보니 여러 행사소식을 빠르게 들을 수 있었고, 이때 학교 수업을 빠지고 가야겠다 결심했던 기억이 난다ㅋㅋ
5. Korea Community Summit 참여
구글 커뮤니티의 모든 오거나이저들이 모여서 회고하는 자리였다. DSC Lead로 참석하였으며, 올해에 대한 회고와 본인 소개, 내년에 대한 계획을 세울 수 있는 자리였다. 많은 GDG, GDE 분을 뵐 수 있었고, 조언을 얻을 수 있었다. 구글 코리아 오피스에서 다양한 액티비티를하면서 많은 분들과 이야기를 주고받을 수 있는 기회가 있었다.
DSC Ewha 친구들과 함께 구글에서하는 팀 알고리즘 대회인 해시코드에 참가했다. 하루를 꼬박 새서 참가했었다. 개인적인 활동이었는데, 앞으로 알고리즘 더 열심히 공부해서 계속해서 참가하고싶다.
7. DSC korea 네트워킹 데이
DSC Korea Lead들끼리 준비했던 행사이다. DSC Korea의 총 12개 학교의 학생들이 모두 모여 레크레이션도 하는 등 네트워킹 행사를 개최했다. 행사를 준비하는 Lead들끼리 장소 대관, 회계, 홍보, 프로그램 구상등 으쌰으쌰하면서 준비했다. 덕분에, DSC 행사중 가장 만족도가 높았던 행사로 피드백을 받았었다.
행사를 준비할 때 연사자 초청, keynote 준비, 아이스브레이킹 자료 준비, 참가한 멤버들의 그룹 토론 주제 준비, 디자인, 장소섭외 등 정말 많은 부분을 하나하나 준비하면서 리드들끼리 더 돈독해지기도 했다. 이때 구글 데브렐, GDE분들이 많은 도움을 주셨던 기억이난다.
DSC 이름으로 걸고하는 가장 큰 행사였으며, 예산처리, 실제 인원체크, 장소 확인 및 준비 등 작은 이벤트를 하기 위해서도 많은 사람들이 노력이 필요함을 다들 체감했다. 원래는 해커톤도 개최하려 했으나 역시나 코로나-19로 인해 이게 DSC 전체의 마지막 행사가 되어서 아쉬웠다..
활동 마무리
활동 막바지 리드들과 함께 활동 후기 릴레이를 작성했다. 작성을하면서 우리 다음으로 활동을 할 리드들이 어떤분들일까 궁금하고, 우리가 아닌 다음 사람들이 할 새로운 활동들이 어떤걸지 설렜었다.
인수인계
2019-2020 DSC Ewha Lead를 마치고 9월중 인수인계 자료를 만들었다. 이때 인수인계 자료를 만들다보니 그동안의 활동을 정리했었는데. 그렇게 블로그 글을 쓰게되었다ㅋㅋㅋㅋ 그런데 이 글을 지금 마무리 하게 될 줄이야.. 현재 DSC Ewha는 다른 리드가 이어받아 운영을 하고있다. 너무 잘하는 친구이지만 코로나때문에 현재 활동이 어떻게 진행되고있는지는 알지못한다ㅠ
DSC는 기존에 하던 개발 동아리, 개발 대외활동이 아닌 데브렐관련 활동도 하면서 나에게 새로운 경험을 주었기 때문에 잊을 수 없는 활동이다. 이때 만났던 인연들은 아직까지도 다들 연락하고 서로 으쌰으쌰하면서 지내고있다 :)
솔직히 좋은일만 있던건아니다. 커뮤니티 오거나이저로 여러 곳과의 커뮤니케이션 문제, 개발자로서의 개인 계발 부진에 대한 고민 등 힘들었던 점들도 많지만 너무 소중한 인연들을 만날 수 있었고 식견을 넓힐 수 있었던 너무 좋은 경험이었다. 따라서 대학교때 했던 활동중 가장 잘한일이라고 생각하고있다.
My one-year DSC activity from August 2019 to August 2020 has come to an end.
I kept telling myself I should write up a summary of my activities, but I kept putting it off and now it's already October lol. I did write about DSC in a previous retrospective, but it was a bit summarized.
Let me look back on the past year of activities starting now.
Application and Acceptance
I happened to learn about DSC through a connection I made during the previous Smilegate Winter Camp — a senior introduced the program to me. The thing is, the deadline was that very day, but somehow I managed to scramble and put together an application, and I got accepted.
The acceptance process consisted of two steps: 1. Document submission 2. Hangouts interview. I've heard that the current process has one more step added.
1. Document Submission
The questions were about leadership/impact and development experience.
Leadership Questions
Upload an introductory video of yourself to YouTube
Questions about your experience leading a development project
Questions about your experience participating in Google Community programs
Questions about your presentation experience: audience, topic, event, number of attendees
Questions about your training experience teaching others: audience, topic, number of attendees
The leadership questions were as listed above, and I filled out all of them except for number 3. (At the time, I didn't even know GDG existed.) There were so many questions and no character limit, so rather than trying to perfectly answer every single one, I focused on highlighting my strengths through my actual experiences and wrote in detail.
This is something I always tell people who come to me for advice, whether it's for lead applications or through other channels — I don't think you need to voluntarily bring up your weaknesses first. Why not? Is that really a weakness? That's the attitude I take, and I just go on and on about my strengths lol
So rather than emphasizing how connected I was to the Google community, I wrote my application with the confident tone of: I've spoken in front of people, I've taught others, I have plenty of project experience, and this program is definitely a great fit for me.
By the way, the introductory video I made for item 1 is saved as a private video on my YouTube channel lol: I'm never showing it to anyone... Since I had to write the application AND upload the video on the same day, I just turned on my webcam and recorded without any editing — looking at it now is so embarrassing... lol If I had to do it now, I'd put my old video team experience from LikeLion to use, rent a gimbal, write a camera movement scenario, and make it a masterpiece of a video..
Development Experience Questions
GitHub profile link
Android app development experience and project description
Web development experience and project description
Cloud development experience and project description
Machine learning development experience and project description
For the development experience section, I was honest about what I didn't know and focused on showcasing what I was good at. Back then, I talked about AWS for my cloud experience lol... oops haha Since DSC activities include receiving newsletters about related technologies and learning videos on various tech topics, I think this question was meant to gauge what fields applicants are interested in.
At the time, I thought development skills would be super important, so I remember heavily emphasizing only my dev skills. But after a year, the actual activities showed that leadership was far more important. So if I were to apply for lead again now, I'd focus more on the leadership section. As for development experience, your preferred languages and frameworks are already visible on your GitHub profile anyway, so it's really about your attitude toward development.
2. Hangouts Interview
You do a 1:1 interview with the Google Korea DevRel team. I still remember being so excited and enthusiastically saying I wanted to do this and that.
The interview mainly asked about leadership and personal experiences rather than technical questions. It's been a year so I don't remember well, but I vaguely recall the gist of what I said when asked what kind of contribution I wanted to make.
I'm in the Computer Science department, and through various extracurricular activities and projects, I discovered what I enjoy and have been continuously studying. But when I look at many of my peers, even though they're in Computer Science, they only focus on coursework and lack experience, so they often don't know what field they actually enjoy. I believe it's important for students to have hands-on experience building things themselves, and I think those accumulated experiences create more opportunities to explore their career paths. That's why I'm confident that DSC can be an opportunity for students at our university to gain more development experience.
So throughout my DSC activities, I focused on enabling members to spend a year dedicated to completing their own projects.
They also asked me about blogging, and I mentioned that I wanted to spread the word about our activities through a blog. In practice, we actually used a team blog, and you can find traces of DSC scattered throughout my blog lol Wow... back then I only had like 3 posts on my blog... lol I'm glad I've come a long way after a year!! Hehe
3. Acceptance Email
After the interview, I first got a call from Google Korea, and then officially received the acceptance email :) At the time, I was so amazed to receive an email in English that I translated it word by word and read it carefully haha. Now English emails are no big deal!!! Actually, they're still not easy
4. Messages I Received During the 2020 Lead Application Period
During this round of lead applications, I received various questions from people. One person particularly stands out in my memory. They asked about my development level and situation at the time — things like how much I managed my project experience, GitHub profile, etc.
My response was that every lead has different specs and different fields they enjoy, so not everyone can have the same portfolio — what's important is highlighting what you want to do. In fact, among our leads, there were non-CS majors too, and everyone had different interests. While I was all-in on backend, Jeongdong initially had interest in ML but became a Cloud all-in player after his DSC activities.
As for lead activities, it's not something where you receive technical training from someone — you have to create development-related activities on your own. So when applying for lead, what's important to convey is your plan: what kind of people you want to meet through this program and how you want to contribute.
At the end of the day, being a lead is about taking initiative rather than being told what to do, so I think showcasing your proactivity is more helpful than showing off how impressive your technical skills are. After all, Googlers already have that technical prowess, so as university students, let's show them our passion!!🔥🔥
The Beginning: Google Korea Onboarding
My very first activity was the onboarding program. Nowadays, onboarding is also done via Hangouts because of COVID. But at the time, we got to visit the Google Korea office, receive a proper introduction to DSC activities, and meet leads from different regions. I wrote about it in great detail before, so I'll attach the link below.
Since I was part of the very first cohort, we were setting up various DSC Korea Facebook pages, Slack channels, and KakaoTalk groups from scratch, preparing the foundation of a new community. Having to create everything from a blank slate with no established structure made things tough for everyone in the beginning — we all had a lot of concerns about whether we were doing it right and what we should be doing.
So about one to two months after the onboarding, we leads started having bi-weekly Hangouts video meetings to share how activities were going at each university. Events that got a good response at other schools would then be adopted at ours too.
I miss you all so muchhhh ㅠㅜㅠㅠ
DSC Ewha Begins with the New Semester
Doing everything alone was no easy task. Poster design (at the time I was participating in a hackathon in China, and during the awards ceremony I was working on the DSC Ewha poster), writing promotional copy, finalizing the curriculum, interviews, selection — I did every step by myself.
I selected all members first and then picked core members (executive team) from within the group. Looking back, I think it would have been better to recruit core members first and discuss what activities to do together.
Promotional posters and copy we prepared at the time
For the interviews, I tailored questions to each applicant's individual application. Like any organization, I believe the most important thing in an interview is understanding why this person wants to join our organization.
There are many developer clubs out there — why specifically DSC Ewha?
If you want project experience, you don't need DSC for that — so why here?
What do you hope to look like one year from now?
Whether they maintain their GitHub
These were the four main questions I asked. One mistake I made was accepting all 22 out of 22 applicants. My thinking at the time was that people would feel responsible for something they applied to on their own, and the number happened to match what I had in mind. However, in hindsight, this decision seems to have been causally related to the high dropout rate later on, which is one of my regrets.
A Year with DSC Ewha Members
In reality, most of the DSC activities were concentrated in 2019. COVID made the 2020 activities ambiguous, and with everyone working online, it was hard to stay motivated. If it weren't for COVID in 2020... I wonder if we could have done so much more with the members.
Since we were at a university, we ran things in a club format.Our main activities were seminars + a year-long team project. In the second semester of 2019, we held regular meetups that included team time + seminar time. After COVID hit, team time was left up to each team's discretion, and seminars shifted to being uploaded on the team blog.
1. Team Projects
I didn't want DSC to become a format where I just led a single course. If I taught a Spring Boot class, members could focus systematically on one common technology, but I thought they wouldn't get to try various technologies when it came time for projects. And honestly, if that's all it was going to be, why bother with DSC when you could just start a web development club?So I let members freely choose whatever technologies they wanted for their projects.
At the time, I said I wouldn't participate in team projects since managing everything was already overwhelming — and it was the right call. Even though I quit all extracurricular activities in my junior year first semester, in the second semester I was juggling a capstone project + DSC Ewha activities + Java Spring study group + coursework, so I really didn't have the bandwidth. And then I did a Naver internship in my senior year first semester, so it would have been truly impossible to focus.
For team projects, on the first meeting day members pitched ideas and then formed teams around the ones they liked. After that, they had team meetings at every weekly gathering, and each team took turns writing about their progress on the team blog. We also had quarterly presentations (once every 3 months) from each team, but due to COVID, only the second semester presentations were in-person — the winter break, first semester, and summer break presentations all had to be done via Hangouts.
PPT slides from the quarterly presentations
I barely provided any technical help. The members independently studied Android and React within their teams, and the projects from this program were the result of their self-driven efforts. Watching their quarterly presentations, I was impressed seeing them work through everything step by step — from design to backend architecture, frontend communication, to model training and data collection. I hope that the experience of going from project planning to completion became something that helped each member's career.
A total of 4 projects made it to the completion stage, and among them, Team Handleang's project was submitted to the DSC Solution Challenge. It would have been great to get good results, but unfortunately the winning team from DSC Korea was from Soonchunhyang University. The source code is collected on GitHub (github.com/DSC-Ewha) and was handed over to the next lead.
2. Mini Seminars
There are so many different fields in development. A common concern among students was not knowing what they actually enjoy. One reason for this is that they simply don't know many technologies and fields exist. They don't even know about learning methods or tools. So I thought about what kind of pathway could help people gain broad, shallow knowledge — at least knowing that certain technologies and tools exist.
So I created a time for members to share what they'd personally studied about the technologies they mainly use, or the technologies used in their team projects. Since each team project used different technologies, I figured this could be a way for members with common interests to connect and discuss further.
So at each weekly meetup, members took turns giving about 15-minute mini seminars. Initially, we filled the DSC Ewha Facebook page (www.facebook.com/DSCEwha) with simple Q&A-style posts about each seminar.
However, when COVID forced us to move team projects and meetings online, the mini seminars also went online. We switched to a format where members recorded videos and uploaded them to the team blog. There were many topics that were new to me too. I was worried this activity might also get scrapped due to COVID, but the members showed great responsibility and everyone uploaded their content, so we were able to wrap it up successfully.
(Left) Seminar card news from meetups (Right) Video uploaded to blog from online seminars
It wasn't all good times during the activities. When we were meeting in-person, I had to prepare for weekly meetups while also coordinating other DSC events, so there was a lot to manage. Because of that, I regret not being able to focus on my own development studies during my junior year second semester due to community activities. On the other hand, when we moved online, some members dropped out and there was free-riding, which really made me doubt my own abilities. During that time, since we were doing team projects online, I regret not providing more care and support as a lead.
Despite being such an inadequate lead, I was able to wrap up activities well and complete the handover thanks to the members who actively participated in every project and seminar.
Wow... I wrote up to here in October and now I'm opening the text editor again to finish it in December lol. Well, at least there's meaning in getting it done!!
Events Held at DSC Ewha
1. DSC Ewha Open Codelab "Mo.Gat.Co" (Let's Code Together)
How the Event Came About
Many students have tried coding through basic SW education courses, but that's ultimately just at the language level.We organized a fun, interest-driven coding session to show students who are curious about coding what you can actually do with the programming languages they learned in elective courses.We decided to plan this event because Google Codelabs had excellent tutorial-style explanations for beginners, and I thought it would be a great opportunity to build DSC Ewha's presence within Ewha Womans University.
Event poster and Facebook posting from the time
Event Preparation Process
During preparation, DSC Ewha members participated as tutors so I didn't have to do everything alone. Fortunately, many members were willing to help, so we had fun preparing the event together. As a lead, my role was more of an administrator than a developer — I handled things like contacting Ewha Womans University's Software-Centered University Initiative to secure funding for the event.
Running the Event
Each session ran for one class period at our university. Out of the total 1 hour and 15 minutes, 5 minutes were for an OT introducing DSC Ewha and DSC Korea, 60 minutes were for hands-on codelab practice with tutors, and 10 minutes were for requesting survey responses.
For the codelabs, we divided roles into main tutors and sub-tutors. The main tutor ran the codelab live in the front, while sub-tutors helped students with any parts they didn't understand, providing additional explanations and assistance.
We held the event twice (2 sessions per event, 4 sessions total), and from what I remember, it was in October and December. In December, several professors even came in to observe... haha. The feedback from the post-event surveys was all positive, so everyone felt really proud. Originally, when I was still lead earlier this year, we planned to keep hosting these events, but due to COVID and everyone struggling to adapt to the online format, we had to cancel everything and unfortunately couldn't plan anything further.
The positive feedback we received at the time.. haha
2. Ewha Garden Committers
This was an internal event within DSC Ewha. We ran a daily commit group for 70 days spanning winter break and the start of the first semester. We referenced the activity format that DSC HUFS had done, and set the specific direction based on "Jandicon," an event held by GDG Pangyo. Our group had 9 members total, and 7 of them participated for all 70 days.
To monitor each other's daily commits, we used Slack's GitHub plugin so that any changes to each person's repository would trigger a notification. Of course, we also set fines for missing a commit.
I also personally redeployed the commit management tool created by GDG Pangyo's "Gardeners" for our Ewha Garden Committers, and built a site to collect commit records. The related code is saved at https://github.com/mjung1798/ewha-daily-commit.
Originally, on the last day of the 70-day period, March 28th, we planned to hold a conference at school called "Ewha Jandicon" where we would present our daily commit activity records to fellow students. But once again, COVID put an end to that plan, which was disappointing.
Tools we used to manage commits!
With these management tools, we ran a commit group where everyone tried to make one commit per day. It would have been hard to keep it up alone, but doing it together like this was really helpful for me too.
Reflections after completing the Ewha Garden Committers program
All the activities at Ewha that I planned and managed received great responses, but I always wanted to do them N more times. Unfortunately, with the chaotic situation in 2020, the main activities only happened in 2019, which is a shame.
Personally, when running Mo.Gat.Co, I focused less on deep technical exploration of technologies I wanted to study, and more on making it an event to promote the club and spark interest in coding. So the content was geared more toward beginners than toward things that would help me technically. (After all, events targeting the general public tend to attract the most people when aimed at beginners.) As a result, preparing these events didn't bring any technical growth to my developer career. When running DSC Ewha, the resources spent on organizing events and managing the club far outweighed the resources invested in improving my own technical expertise, which is something I feel regretful about.
However, the Committers activity was one I was very satisfied with, because I was also doing one commit per day, building a habit of consistently studying development. It gave me dedicated time purely for my own technical growth through actual development studies.
My Activities in Connection with the Google Community
Communication with the Google Korea Developer Relations team was essential while doing DSC activities. This was because DSC Korea was launched for the first time in 2019, and there were also collaborations with other Google-supported communities beyond DSC. Naturally, I ended up participating in and collaborating on events hosted by GDG, WTM, and GDE members.
First, I participated as staff at DevFest organized by GDG Seoul to see how events were run, and from that point on, I started planning and thinking about the DSC events I would create. I've written a detailed review at the link above. (I should've been documenting things properly all along..haha)
The Google Korea DevRel team gave attendance passes to DSC Korea Leads, so I got to attend. Up until then, I'd honestly only used AWS and didn't really use GCP lol. I think this was when I started gradually trying out GCP. Luckily, I wrote a review about it back then.
GDG Campus Korea reached out to DSC Korea Leads with a proposal to co-organize a DevFest together. Since our university ended up being the venue, I got even more actively involved in this event.
I went on my own because I was excited about the hands-on sessions. Being a Lead meant I could hear about various events quickly, and I remember deciding to skip class to go to this one lol
5. Korea Community Summit Participation
This was a gathering where all the organizers of Google communities came together for a retrospective. I attended as a DSC Lead, and it was a chance to reflect on the year, introduce ourselves, and plan for the next year. I got to meet many GDG and GDE members and receive their advice. At the Google Korea office, we did various activities and had plenty of opportunities to chat with lots of people.
I participated in HashCode, a team algorithm competition by Google, together with friends from DSC Ewha. We pulled an all-nighter for it. It was a personal activity, but I'd love to keep participating after studying algorithms more seriously.
7. DSC Korea Networking Day
This was an event organized by the DSC Korea Leads together. Students from all 12 DSC Korea universities gathered for a networking event that included recreational activities. The Leads worked together on venue booking, budgeting, promotions, and program planning, cheering each other on throughout. Thanks to everyone's efforts, this event received the highest satisfaction feedback among all DSC events.
While preparing the event — inviting speakers, preparing keynotes, creating icebreaker materials, preparing group discussion topics for attending members, designing, and securing the venue — we Leads grew even closer through all the work. I remember the Google DevRel team and GDE members helping us a lot during this time.
This was the biggest event held under the DSC name, and everyone realized how much effort from so many people is needed even to organize a small event — budget management, headcount checks, venue confirmation and setup, and more. We had originally planned to host a hackathon as well, but unfortunately, due to COVID-19, this ended up being DSC's last event overall, which was really disappointing..
Wrapping Up Activities
Toward the end of our term, the Leads wrote a relay of activity reviews together. While writing them, I was curious about who the next batch of Leads would be and excited about what new activities they would come up with.
Handover
After finishing my term as the 2019-2020 DSC Ewha Lead, I prepared handover materials around September. While putting together the handover documents, I ended up organizing all my past activities — and that's how I ended up writing this blog post lol. But I never expected to be finishing it this late.. Currently, DSC Ewha is being run by a new Lead who took over. She's really talented, but because of COVID, I'm not sure how activities are going right now ㅠ
DSC was different from the typical dev clubs and extracurricular activities — it gave me new experiences through DevRel-related activities, making it an unforgettable part of my life. The connections I made back then? We're all still in touch, cheering each other on :)
Honestly, it wasn't all sunshine and rainbows. There were struggles like communication issues with various organizations as a community organizer and worries about not growing enough as a developer. But I was able to meet truly precious people and broaden my horizons — it was such an amazing experience. That's why I consider it the best thing I did during my college years.
public interface UserRepositoryCustom {
Optional<User> findByUserId(Long userId);
}
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserRepositoryCustom {
private final JPAQueryFactory jpaQueryFactory;
@Override
public Optional<User> findByUserId(Long userId) {
User user = jpaQueryFactory.selectFrom(QUser.user)
.where(QUser.user.userId.eq(userId))
.fetchOne();
return Optional.ofNullable(user);
}
}
이렇게 짠 Querydsl 코드를 테스트하기 위한 테스트코드를 짤 때 @SpringBootTest를 하면 모든 빈이 주입되기 때문에 상관이 없지만,
아래 코드와 같이 DataJpaTest와 같은 슬라이싱 테스트를 하고싶을 때 문제가 발생한다.
@DataJpaTest
@ActiveProfiles("test")
class UserRepositoryTest {
@Autowired
private EntityManager entityManager;
@Autowired
private UserRepository userRepository;
private User settingUser() {
User settingUser = User.builder()
.email("jyami@ewhain.net")
.name("jyami")
.build();
return userRepository.save(settingUser);
}
@Test
void test() {
settingUser();
entityManager.clear();
User user = userRepository.findByUserId(1L)
.orElseThrow(() -> new ResourceNotFoundException("user", "userId", 1L));
}
}
Error creating bean with name 'userRepositoryImpl' defined in file [/Users/jyami/Documents/commiters/commiters-ewha/commiters-ewha-api/build/classes/java/main/com/jyami/commitersewha/domain/user/UserRepositoryImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.querydsl.jpa.impl.JPAQueryFactory'
결국 이유는 JpaQueryFactory가 persistenceLayer가 아니어서 빈등록이 되지않아 발생하는 문제인데, 이때 테스트 시 특정부분의 빈만 등록해주는 방법이 있었다!
@TestConfiguration
public class TestConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
테스트에서만 사용할 용도의 @TestConfiguration을 이용해 JPAQueryFactory 만 Bean으로 생성해준다.
@DataJpaTest
@ActiveProfiles("test")
@Import(TestConfig.class)
public class UserRepositoryTest {
}
이후 @Import 어노테이션을 사용해 해당 테스트용 빈을 주입해주면, JpaQueryFactory에 대한 빈도 생성되므로, Querydsl의 슬라이싱 테스트가 가능해진다 :)
public interface UserRepositoryCustom {
Optional<User> findByUserId(Long userId);
}
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserRepositoryCustom {
private final JPAQueryFactory jpaQueryFactory;
@Override
public Optional<User> findByUserId(Long userId) {
User user = jpaQueryFactory.selectFrom(QUser.user)
.where(QUser.user.userId.eq(userId))
.fetchOne();
return Optional.ofNullable(user);
}
}
When writing test code for Querydsl code like this, using @SpringBootTest would be fine since all beans get injected,
but a problem occurs when you want to do a slicing test like @DataJpaTest as shown in the code below.
@DataJpaTest
@ActiveProfiles("test")
class UserRepositoryTest {
@Autowired
private EntityManager entityManager;
@Autowired
private UserRepository userRepository;
private User settingUser() {
User settingUser = User.builder()
.email("jyami@ewhain.net")
.name("jyami")
.build();
return userRepository.save(settingUser);
}
@Test
void test() {
settingUser();
entityManager.clear();
User user = userRepository.findByUserId(1L)
.orElseThrow(() -> new ResourceNotFoundException("user", "userId", 1L));
}
}
Error creating bean with name 'userRepositoryImpl' defined in file [/Users/jyami/Documents/commiters/commiters-ewha/commiters-ewha-api/build/classes/java/main/com/jyami/commitersewha/domain/user/UserRepositoryImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.querydsl.jpa.impl.JPAQueryFactory'
The reason is that JpaQueryFactory isn't part of the persistence layer, so it doesn't get registered as a bean. But there's a way to register only specific beans during testing!
@TestConfiguration
public class TestConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
Using @TestConfiguration, which is meant only for tests, we create just the JPAQueryFactory as a Bean.
@DataJpaTest
@ActiveProfiles("test")
@Import(TestConfig.class)
public class UserRepositoryTest {
}
Then, by using the @Import annotation to inject the test-specific bean, the JpaQueryFactory bean also gets created, making slicing tests with Querydsl possible :)
redirect_uri : OAuth2 provider가 성공적으로 인증을 완료했을 때 redirect 할 URI를 지정한다. (OAuth2의 redirectUri 와는 다르다)
2. endpoint로 인증 요청을 받으면, Spring Security의 OAuth2 클라이언트는 user를 provider가 제공하는 AuthorizationUrl로 redirect 한다. Authorization request와 관련된 state는authorizationRequestRepository에 저장된다 (Security Config에 정의함) provider에서 제공한 AutorizationUrl에서 허용/거부가 정해진다.
이때 만약 유저가 앱에 대한 권한을 모두 허용하면 provider는 사용자를 callback url로 redirect한다. (http://localhost:8080/oauth2/callback/{provider}) 그리고 이때 사용자 인증코드 (authroization code) 도 함께 갖고있다.
만약 거부하면 callbackUrl로 똑같이 redirect 하지만 error가 발생한다.
3. Oauth2 에서의 콜백 결과가 에러이면 Spring Security는oAuth2AuthenticationFailureHanlder를 호출한다. (Security Config에 정의함)
4. Oauth2 에서의 콜백 결과가 성공이고 사용자 인증코드 (authorization code)도 포함하고 있다면 Spring Security는 access_token 에 대한 authroization code를 교환하고, customOAuth2UserService 를 호출한다 (Security Config에 정의함)
5. customOAuth2UserService 는 인증된 사용자의 세부사항을 검색한 후에 데이터베이스에 Create를 하거나 동일 Email로 Update 하는 로직을 작성한다.
6. 마지막으로 oAuth2AuthenticationSuccessHandler 이 불리고 그것이 JWT authentication token을 만들고 queryString에서의 redirect_uri로 간다 (1번에서 client가 정의한 ) 이때 JWT token과 함께!
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private CustomOAuth2UserService customOAuth2UserService;
@Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
@Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
@Autowired
private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
@Bean
public TokenAuthenticationFilter tokenAuthenticationFilter() {
return new TokenAuthenticationFilter();
}
/*
By default, Spring OAuth2 uses HttpSessionOAuth2AuthorizationRequestRepository to save
the authorization request. But, since our service is stateless, we can't save it in
the session. We'll save the request in a Base64 encoded cookie instead.
*/
@Bean
public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.disable()
.formLogin()
.disable()
.httpBasic()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/",
"/error",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/auth/**", "/oauth2/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.authorizationEndpoint()
.baseUri("/oauth2/authorize")
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.and()
.redirectionEndpoint()
.baseUri("/oauth2/callback/*")
.and()
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
// Add our custom Token based authentication filter
http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
redirect_uri: Specifies the URI to redirect to when the OAuth2 provider successfully completes authentication. (This is different from OAuth2's redirectUri)
2. When the authentication request is received at the endpoint, Spring Security's OAuth2 client redirects the user to the AuthorizationUrl provided by the provider. The state related to the authorization request is stored in the authorizationRequestRepository (defined in Security Config). The allow/deny decision is made at the AuthorizationUrl provided by the provider.
If the user grants all permissions to the app, the provider redirects the user to the callback URL (http://localhost:8080/oauth2/callback/{provider}) along with the user's authorization code.
If the user denies, they are redirected to the same callbackUrl, but with an error.
3. If the OAuth2 callback result is an error, Spring Security invokes the oAuth2AuthenticationFailureHanlder (defined in Security Config).
4. If the OAuth2 callback result is successful and includes the authorization code, Spring Security exchanges the authorization code for an access_token and invokes the customOAuth2UserService (defined in Security Config).
5. The customOAuth2UserService retrieves the authenticated user's details and then either creates a new entry in the database or updates the existing one with the same email.
6. Finally, the oAuth2AuthenticationSuccessHandler is called, which creates a JWT authentication token and redirects to the redirect_uri from the queryString (the one defined by the client in step 1) — along with the JWT token!
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private CustomOAuth2UserService customOAuth2UserService;
@Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
@Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
@Autowired
private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
@Bean
public TokenAuthenticationFilter tokenAuthenticationFilter() {
return new TokenAuthenticationFilter();
}
/*
By default, Spring OAuth2 uses HttpSessionOAuth2AuthorizationRequestRepository to save
the authorization request. But, since our service is stateless, we can't save it in
the session. We'll save the request in a Base64 encoded cookie instead.
*/
@Bean
public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.disable()
.formLogin()
.disable()
.httpBasic()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/",
"/error",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/auth/**", "/oauth2/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.authorizationEndpoint()
.baseUri("/oauth2/authorize")
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.and()
.redirectionEndpoint()
.baseUri("/oauth2/callback/*")
.and()
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
// Add our custom Token based authentication filter
http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
리트코드에서 매일매일 24시간안에 한 문제를 풀어야하는 과제가 내려오는 형식인데 한국 시간 기준으로는 오후 4시에 새로운 문제가 업로드 된다.
이 챌린지를 계속 하다보니 일일커밋도 자연스럽게 하게 되었다 (지금 깃캣도 키우고있음..ㅎㅎ) 7월 레포지토리 기준 33 커밋이다.
매일매일 문제를 풀고 싶었으나 그럼에도 머리가 안돌아가서 못푼문제, 사람들의 discussion을 봐도 이해가 안되는 문제들은 가볍게 패스했다. 풀다보면 나중에 봤을때 이해가 되는 순간이 있지 않을까 싶어서 그랬다.
7월 챌린지는 84 프로로, 27 / 31 ( solve / total ) 달성 했다.
생각보다 하나씩 해결하는게 재밌어서 8월에도 할 듯?
In my last retrospective, I wrote that I should study algorithms.
I had done a bit of the May Challenge and June Challenge before, but both times I just half-heartedly solved only the problems I already knew how to do (I was too lazy to solve DFS and tree problems, so I'd skip them whenever they came up) and never finished either one.
I wasn't actually keeping up with the algorithm study I mentioned in my retrospective lol But I spent about 2 days studying algorithms to prepare for a coding test, and it was oddly fun, so I decided to jump in.
The format is that LeetCode gives you one problem each day that you have to solve within 24 hours. Based on Korean time, new problems are uploaded at 4 PM.
Keeping up with this challenge naturally led me to make daily commits too (I'm even raising a GitCat right now lol). Based on the July repository, I've made 33 commits.
I wanted to solve a problem every single day, but there were still some problems I couldn't solve because my brain just wouldn't cooperate, and some that I couldn't understand even after reading people's discussions — I just lightly skipped those. I figured that if I keep at it, there'll come a time when I look back and finally get them.
I finished the July Challenge at 84 percent, achieving 27 / 31 ( solve / total ).
It was more fun than I expected to knock them out one by one, so I'll probably keep going in August too!
함수 : 메서드처럼 특정 클래스에 종속되지 않는다. 하지만 메서드처럼 파라미터 리스트 바디, 반환 형식, 가능한 예외 클래스 포함
전달 : 람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있다.
간결성 : 익명 클래스처럼 많은 자질구레한 코드를 구현할 필요가 없다.
b. 람다의 구성
Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight.compareTo(a2.getWeight);
파라미터 리스트 : Comparator의 compare 메서드의 파라미터 (두 개의 사과)
화살표 : 람다의 파라미터 리스트와 바디를 구분
람다의 바디 : 두 사과의 무게를 비교한다. 람다의 반환값에 해당하는 표현식
c. 람다 예제
사용 사례
람다 예제
불린 표현식
(List<String> list) -> list.isEmpty()
객체 생성
()->new Apple(10)
객체에서 소비
(Apple a)->{System.out.println(a.getWeight)}
객체에서 선택/추출
(String s)-> s.length()
두 값을 조합
(int a, int b)-> a * b
두 객체 비교
(Apple a1, Apple a2) -> a1.getWeight.compareTo(a2.getWeight);
2. 람다를 사용하는 곳
함수형 인터페이스 문맥에서 사용
a. 함수형 인터페이스
정확히 하나의 추상메서드를 지정하는 인터페이스
많은 디폴트 메서드가 있더라도 추상 메서드가 오직 하나면 함수형 인터페이스
ex ) Comprator, Runnable
람다는 함수형 인터페이스의 추상 메서드 구현을 직접 전달 할 수 있어 전체 표현식을 함수형 인터페이스의 인스턴스로 취급할 수 있다.
@FunationalInterface : 함수형 인터페이스임을 가리키는 어노테이션, 이를 선언하고 실제로 함수형 인터페이스가 아니면 컴파일 에러 발생
b. 함수 디스크립터(function descriptor)
람다 표현식의 시그너처를 서술하는 메서드
() -> void : 파라미터 리스트가 없으며 void 를 반환하는 함수 : Runnable
(Apple, Apple) -> int : 두개의 Apple을 인수로 받아 int를 반환하는 함수
c. 사용법
함수형 인터페이스를 인수로 받는 메서드로 전달
변수에 할당
3. 람다 활용 : 실행 어라운드 패턴
실행 어라운드 패턴(execute around pattern) : 자원을 열고 -> 처리한 다음(변동) -> 자원을 닫는다
// 자원 열고 닫는 로직이 중복적으로 앞뒤에 있다.
public static String processFile2(BufferedReaderProcessor p) throws IOException {
InputStream fileResourceAsStream = FileProcessor.class.getClassLoader().getResourceAsStream("data.txt");
try(BufferedReader br = new BufferedReader(new InputStreamReader(fileResourceAsStream))){
return p.process(br);
}
}
//처리 로직에 대한 동작을 함수형 인터페이스를 이용해 파라미터화 하였다.
@FunctionalInterface
public interface BufferedReaderProcessor {
String process(BufferedReader br) throws IOException;
}
// 처리 로직에 대한 동작을 선언하여, 실행 어라운드 패턴안에서 해당 동작을 수행한다.
@Test
@DisplayName("실행 어라운드 패턴 개선1")
void name2() throws IOException {
String s = FileProcessor.processFile2(br -> br.readLine());
assertThat(s).isEqualTo("line1");
}
4. 함수형 인터페이스 사용
함수 디스크립터(function descriptor) : 함수형 인터페이스의 추상 메서드 시그너처
공통의 함수 디스크립터를 기술하는 함수형 인터페이스 집합 : Comparable, Runnable, Callable 등 자바에서 포함하고 있다
1. 람다가 사용된 컨텍스트확인 : filter의 정의 확인 2. 두 번째 파라미터로 Predicate<Apple> 형식(대상 형식)을 기대한다. 3. Predicate<Apple>의 추상메서드를 확인한다. 4. Apple을 인수로 받아 boolean을 반환하는 test 메서드다 (Apple->boolean) 5. 함수 디스크립터와, 람다의 시그너처가 일치한다! : 형식검사 완료
b. 같은 람다 다른 함수형 인터페이스
대상 형식이라는 특징덕분에 같은 람다표현식이더라도 호환되는 추상메서드를 가진 다른 함수형 인터페이스 사용이 가능하다.
Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> p = () -> 42;
하나의 람다표현식을 다양한 함수형 인터페이스에 사용할 수 있다.
c. 형식추론
자바 컴파일러
람다 표현식이 사용된 컨텍스트(대상 형식) 를 이용해 람다 표현식과 관련된 함수형 인터페이스 추론
대상 형식을 이용해서 함수 디스크립터를 알 수 있다.
컴파일러는 람다의 시그너처도 추론할 수 있다.
상황에 따라 명시적으로 형식을 포함하는 것이 좋을 때도 있고 형식을 배제하는 것이 가독성을 향상시킬 때도 있다.
// 형식 추론을 하지 않는다
Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
// 형식 추론을 한다
Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
d. 지역변수 사용
자유변수(free variable) : 파라미터로 넘겨진 변수가 아닌 외부에서 정의된 변수 => 람다캡처링(capturing lambda)
제약 : final로 선언되어있거나 실질적으로 final로 선언된 변수와 똑같이 사용되어야한다.
람다 표현식은 한번만 할당할 수 있는 지역변수를 캡쳐한다.
int portNumber = 1337;
Runnable r = ()->System.out.println(portNumber);
portNumber = 31337;
제약이 있는 이유
인스턴스 변수는 힙에 저장, 지역변수는 스택에 저장된다.
람다가 스레드에서 실행된다면 변수를 할당한 스레드가 사라져서 변수 할당이 해제되었는데도 람다를 실행하는 스레드에서 해당 변수에 접근하려 할 수 있다.
원래 변수에 접근을 허용하지 않고 자유 지역 변수의 복사본을 제공한다
복사본의 값이 바뀌지 않아야한다 == 지역 변수에는 한 번만 값을 할당해야 한다.
e. 클로저
클로저 : 함수의 비지역 변수를 자유롭게 참조할 수 있는 함수의 인스턴스
설명!
클로저
람다
다른함수의 인수로 전달할 수 있다.
O
O
자신의 외부 영역의 변수에 접근할 수 있다.
O
O
람다가 정의된 메서드의 지역변수 값을 바꿀 수 있다.
O
X
6. 메서드 레퍼런스
특정 메서드만을 호출하는 람다의 축약형이다.
명시적으로 메서드명을 참조하여 가독성을 높일 수 있다! 메서드 명 앞에 구분자(::)를 붙이는 방식으로 활용한다.
Apple apple = new Apple("red", 100);
Supplier<Integer> supplierLambda = () -> apple.getWeight();
Supplier<Integer> supplier = apple::getWeight;
메서드 레퍼런스는 컨텍스트의 형식과 일치하는지 확인한다.
b. 생성자 레퍼런스
ClassName::new 처럼 클래스와 new 키워드를 사용해 기존 생성자의 레퍼런스를 만들 수 있다.
@Getter
public class Apple {
private String color;
private Integer weight;
public Apple() {
}
public Apple(String color) {
this.color = color;
}
public Apple(Integer weight) {
this.weight = weight;
}
public Apple(String color, Integer weight) {
this.color = color;
this.weight = weight;
}
}
// 기본 생성자 레퍼런스
Supplier<Apple> appleSupplier = Apple::new;
//인수 1개 생성자 레퍼런스 : 무게
Function<Integer, Apple> appleFunction = Apple::new;
// 인수 1개 생성자 레퍼런스 : 색깔
Function<String, Apple> appleFunction = Apple::new;
// 인수 2개 생성자 레퍼런스
BiFunction<String, Integer, Apple> appleBiFunction = Apple::new;
시그너처를 대응시켜서 생성자에 접근이 가능하다.
7. 람다, 메서드 레퍼런스 정리
코드 전달 : 함수형 인터페이스를 구현하여 사용
익명 클래스 사용 : 클래스를 구현하지 않고 바로 인스턴스 화 할 수 있으나 코드가 지저분하다.
람다 표현식 사용 : 추상 메서드의 시그너처(함수 디스크립터)가 람다 표현식의 시그너처를 정의한다 = 형식추론에 이용
메서드 레퍼런스 활용 : 람다 표현식의 인수를 더 깔끔하게 전달할 수 있다.
8. 람다 표현식을 조합할 수 있는 메서드
람다 표현식을 조합할 수 있는 유틸리티 메서드 : 디폴트메서드를 사용한다
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2); // 추상메서드
default Comparator<T> reversed() {...}
default Comparator<T> thenComparing(Comparator<? super T> other) {...}
}
실제로 위와 같이 선언이 되어있다.
단순한 람다 표현식을 조합해서 더 복잡한 람다 표현식을 만들 수 있다.
a. Comparator 조합
inventory.sort(
Comparator.comparing(Apple::getWeight)
.reversed() // 역정렬
.thenComparing(Apple::getColor)); // 두번째 비교자를 만들 수 있다 (두 사과 비교 후 같을 때 정렬 법)
b. Predicate 조합
Predicate<Apple> redApple = a -> "red".equals(a.getWeight());
// 기존 Predicate를 반전
Predicate<Apple> notRedApple = redApple.negate();
// 기존 Predicate에 and를 이용해서 빨강이면서 무거운 사과로 조합
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);
// 기존 Predicate에 or를 이용해서 빨강이면서 무거운 사과 또는 그냥 녹색사과로 조합
Predicate<Apple> redAndHeavyAppleOrGreen = redApple
.and(a -> a.getWeight() > 150)
.or(a -> "green".equals(a.getColor()));
c. Function 조합
andThen : 주어진 함수를 먼저 적용한 결과를 다른 함수의 입력으로 전달하는 함수를 반환
compose : 인수로 주어진 함수를 먼저 실행 한 후에 그 결과를 외부 함수의 인수로 제공
@Test
@DisplayName("Function 연결")
void name3() {
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g); // g(f(x))
int result = h.apply(1);
assertThat(result).isEqualTo(4);
}
@Test
@DisplayName("Function 연결")
void name4() {
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g); // f(g(x))
int result = h.apply(1);
assertThat(result).isEqualTo(3);
}
댓글
Comments