1. 상황

휴면회원에 대한 기록을 쌓는 배치 프로세스에 대하여 쿼리를 작성 중이었다. 프로세스 흐름도는 다음과 같다.

 

 

2. 문제점

    - 연관성이 없는 각 select에 대해 단일 스레드를 통해서 순차적으로 쿼리를 실행하였기 때문에, 현재 상황보다 더 많은 테이블에 대해 작 

       업을 할 경우, 많은 시간이 소모된다.

    - @Transactional이 동작하는 사이클에 대해서 제대로 알고 쓰지 못하였기 때문에, rollback이 제대로 되지 않고 있다.

    - Spring Boot Batch Starter가 있음에도 불구하고 일반 절차 함수를 작성하듯이 프로그램을 작성하였다.

    - 후에 내가 작성한 프레임워크에 다른 배치 프로세스가 추가적으로 작성하는 것을 잘 몰랐기 때문에, 확장성이 덜 고려된 공통 코드를            작성하였다.

    - 작성한 프레임워크는 jar파일로 build되어 

   

 

 

 

3. 개선(해야할)점

    - 상호 관련이 없고, 연계성이 없는 쿼리의 경우는 단일 스레드에서 작업하지 않고, 복수개의 스레드에서 작업하여 속도를 향상시킨다.

    - 자체 코드 리뷰 결과, @Transactional을 사용한 @Service 내부의 method에서 catch문에서 exception을 던지고 있지 않기 때문에, 

       Spring에서는 exception을 감지하지 못하고, rollback를 시키지 않는 점이 발견되었다. 각 catch 문에 대해서 마지막에 exception을

       던져서 rollback이 가능하게 한다.

    - AOP에서 배치프로세스에 대한 로그를 작성할 때, @Transactional 어노테이션 걸린 서비스에 대해 동작하도록 되어있는데, 이를 조금 

      더 적절한 방법으로 대체할 수 있으면 대체를 하는 것이 좋다.

    - 불필요하게 Embadded Tomcat과 서버가 실행되는 것을 disable하여 jar 파일이 실행되기 전 준비단계를 줄였다.

    - JVM이 running 되기 전 단계에서 프로세스를 처리하도록 ApplicationListener를 이용하여 ContextRefreshedEvent에 대해 핸들링   

       하였다. 그러나, ContextRefreshedEvent의 경우는 Context가 바뀌는 상황마다 ApplicationListener가 호출되므로, spring이 시작할 

       때, 단발성으로 호출되도록 ApplicationStartingEvent같은 것을 사용하는 것이 더 좋을 듯하다.

    - 실행파일 형태로 cron에 걸수 있도록 설계하라하여 지금 상황과 같이 설계하였다. 그러나, 현행방법과 데몬이 계속 떠있는 방식 중 어   

       떤것이 더 효율적일까를 따져봐야 할 것이다.

'회고' 카테고리의 다른 글

20200624 회고 - 배치 프로세스 및 데이터베이스  (0) 2020.07.28
2019 회고  (2) 2020.02.01
20191126 회고 - 프로그래밍 패턴  (0) 2019.11.26

1. 올해 첫 직장에 입사하며 다룰 수 있었던 기술

 

 

1-1. AWS

  • 기존의 알던 온프레미스 환경에서 벗어나 클라우드를 접하였다.
  • 쓴만큼 지불한다는 개념이 존재하였기 때문에 각 클라우드 제품을 사용하는 것에 대한 최적화, 기반 기술, 한계점, 단위 비용에 대해 명확히 알고 있어야만 설계가 가능했다.
  • 그러므로, 일반 개발자보다 훨씬 더 많은 학습을 통해 deep하게 공부를 할 필요성을 인지하였다.
  • 중점적으로 사용한 서비스로는 Lambda, EC2, S3, API Gateway, EKS이다.
  • 2020에는 EKS, MSK, Elastic Search 등 더욱 많은 서비스를 경험하여 내 것으로 만들 것이며 기반 기술에 대해 심도 있는 공부를 할 예정이다.

 

1-2. Kubernetes & Docker

  • 입사 후 AWS Dev-Day를 통해 가장 처음 접한 지식이다.
  • 컨테이너라는 단어만 알고 있었지만, 이 컨테이너의 의미와 어떻게 관리를 하는지에 대해 알 수 있었다.
  • 컨테이너를 통한 관리의 한계 또한 학습하여 상황에 맞게 쓰도록 노력해야할 것이다.
  • 현재 이에대한 우선순위가 낮아져 공부가 pending 된 상태이다.
  • AWS EKS를 통한 deploy대신, 모두 메뉴얼로 구성을 하면서 느낀 점에 대해 정리를 해볼 생각이다.

 

1-3. Apache Kafka

  • 가장 처음 접한 메시지 큐 오픈소스이다.
  • 지금까지 메시지가 direct하게 전달되거나 DB에 저장되어서 query를 통해 데이터를 임시 버퍼로 저장하는 절차로 알고있었지만,
  • push, pull형태로 topic라는 키워드를 가지고 대용량 데이터를 주고 받았다.
  • NHN에 데이터를 중계하는 프로젝트의 샘플 워크플로우를 작성하며 producer와 broker에 대해 공부를 할 수 있었으며
  • 설정 파라미터애 따라 어떻게 통신이 달라지는지에 대해 명확히 알 수 있었다.
  • 향후 대용량 메시지 관련 프로젝트 참여 시, 이에 대한 지식을 가지고 가이드 할 수 있도록 학습을 해야한다.

 

1-4. API Gateway & lambda 기반의 Serverless APP

  • 기존 서버상에서 APP을 deploy하게되면 확장성, 보안성 등 많은 부분에 시간과 비용이 소모된다.
  • 이에 대해 부담을 줄이고 개발에만 집중 할 수 있기 때문에 이러한 점에 대해서는 장점이지만,
  • FaaS(Function as a Service) 제공사에 많은 의존을 하게 되며
  • AWS같은 경우 하나의 lambda function 당 500MB의 /tmp storage와 3GB의 메모리라는 제약사항이 존재한다.
  • Serverless 이기 때문에 localstorage를 사용하는데 제약사항이 있다.
  • 정리하면 제약사항에 대해 deep하게 파악하고 있어야 하며, 필요한 부분에 대해서만 사용해야한다.

 

 

1-5. DynamoDB & MongoDB

  • 입사 후 대용량 데이터 처리에 관한 이론을 공부하다 처음 알게 되었다.
  • RDB에 비교하여 attribute(schema)에 자유로운 형태를 지니며, 자유롭게 필드를 삽입 할 수 있다.
  • 이를 이용하여 빠른 개발을 할 수 있다는 장점이 있으나, 관계가 없는 DB이기 때문에
  • Join연산에 약하며, 데이터에 대한 update시 모든 table에 대해 관계가 있는 데이터에 접근해야한다.
  • 그 결과, RDB에 비해 상대적으로 데이터무결성에 약할 수 있다는 단점이 있다.
  • 절대적으로 이게 낫다는 것이 아닌 서로 상호보완적 관계이기 때문에 상황에 맞는 데이터베이스를 사용해야한다.
  • 특히 DynamoDB는 다른 NOSQL 엔진에 비교하여 RCU, WCU라는 개념이 존재하기 때문에 이에 대한 최적화를 신경써서 하지 않으면...
  • 요금 폭탄을 맞게 된다. 그러므로 처음 설계시 Partition Key와 Sort Key를 잘 설계하여 인덱스와 scan연산을 덜 쓰도록 해야한다.

 

1-6. Spring Framework & Oracle DB

  • 신입사원 교육으로 6개월 중 각각 1개월씩 Spring Framework, Oracle DB에 때한 교육을 수강하였다.
  • 위 2개의 대한 존재만 알고 있고 건드려본적이 없기 때문에 이를 학습하는 2개월이 작년에 가장 힘들었으며, 시간 투자가 많았다.
  • 사수님께서 위 2개는 SI에서 보편적으로 가장 많이 쓰는 것이기 때문에 deep하게 알아두는 것이 좋다고 하셨다.
  • 그러나 교육을 들을 당시 신입사원 프로젝트를 찍어내는데에 급급하여 spring framework에 대한 아키텍처와,
  • Oracle DB query최적화에 대해 제대로 학습을 하지 못하여 매우 아쉽다.
  • 올해 시간이 날때 조금씩 학습을 하여 상황에 맞는 framework를 사용할 수 있도록 가이드라인을 제시하도록 하겠다.
  • 어떠한 프로젝트에 들어가도 준비된 사람이 되도록 하겠다.

 

 

 

2. 올해 이룬 목표

2-1. AWS Solution Architect Associate 취득

 

2-2. 최소 하나의 프로젝트에 참여하여 기여하기

 

 

 

2-3. 블로그 open하여 한달에 100명 이상의 유입자수 달성 (2019 11월 블로그 오픈)

 

 

 

2-4. 누군가의 멘토가 되어 멘티가 주니어개발자가 되도록 도와주기

  • 학교 한 후배에게 가이드라인을 지속적으로 제시해주고 있으며, 졸업 작품 설계에 대해 지속적으로 관찰 중.
  • 회사를 다니며 알게 된 개발자로써의 자세와 필요한 역량에 대해 알려주며
  • 함께 공부를 하며 성장해나가고 있다.

 

 

 

 

 

3. 올해 이루지 못한 목표

3-1. AWS Solution Architect Professional 취득 실패

  • 공부를 함에 있어서 시간 분배를 실패하였다.
  • 지속적으로 Architeecture에 대해 학습하고 시험 대비를 해야했으나, 준비가 부족하여 1/31에 있던 시험을 3월 2째주로 연기하였다.

 

 

3-2. 정보보안기사 취득 실패

  • 대학교 졸업 전 취득이 목표였으나, 우선순위를 AWS학습과 회사 적응에 둠에 따라 자연스럽게 공부를 하지 못했다.
  • 2020 실기를 보기 전 자격이 소멸될 예정이다.
  • 클라우드를 학습하며 다져진 지식을 기반으로 학생이 아닌 개발자로써의 시각으로 재접근하여 천천히 공부 할 예정이다.

 

 

3-3. 인간답게 살지 못함.

  • 학교를 졸업 후 급작스러운 환경변화가 있었다. (청주 → 서울)
  • 너무 조급한 하루의 대부분을 학습하는데에만 시간을 투자했다.
  • 1일 1식을 함에 따라 운동을 하여도 제 몸을 유지 할 수 없었다. (72kg → 현재 63kg)
  • 스트레스로 폭식하여 불어난 체중을 되돌리는데 성공하였으나, 급작스런 체중변화에 근손실도 존재하였다.
  • 결과론적으로 체력이 떨어졌음.
  • 정작 나에대한 투자를 하지 못하였기 때문에 2020은 나에 대한 투자를 늘릴 계획이다.

 

 

4. 근무간 팀장님의 나에 대한 평가 (실제원본이다)

 

더보기

정형님은 업무를 수행하는 데 있어서 열정 있고 책임감 있으며 성실한 인재입니다. 본인이 계획한 일정에 맞춰 업무를 수행할 수 있도록 업무 시간 외에도 개인의 시간을 투자해 업무를 완수합니다.

또한, 개발자로서 기본적인 소양인 새로운 기술에 대한 호기심과 문제 해결을 위한 성실함과 끈기를 가지고 있는 좋은 인재입니다. 본인의 임무를 완수 후에 기다리지 않고 팀의 업무에 도움이 되거나 개인의 역량을 개발하기 위해 솔선수범하는 적극적인 모습을 보입니다.

다만, 본인이 노력하는 만큼 자신이 습득한 기술이나 지식에 관해 주장이 강해 자칫 잘못된 결론에 도달할 위험성도 있습니다. 신입 사원인 만큼 아직 다양한 서비스 개발 및 운영 경험 부족으로 인해 섣부른 결론을 내지 않도록 본인의 겸손한 태도와 차분한 의사 전달력이 필요합니다.

또한, 조급한 성격으로 의사전달에 있어서 상대방의 반응이나 참여자의 분위기를 고려치 않고 발언하는 경우가 있어 주의할 필요가 있습니다. 물론, 기술 멘토나 리더가 논리적인 지도와 토론으로 긍정적인 결론을 이끌어 나아가면 구성원에게 좋은 영향을 미치겠지만, 항상 그런 상황이 함께할 수는 없음으로 상대방을 배려하는 태도를 익히도록 해야 합니다. 더불어 자신이 알고 있는 지식에 대한 발표 능력과 주변 사람들과의 지식 전달을 매끄럽게 할 수 있도록 공감 능력을 좀 더 키워 나아갔으면 합니다.

 

  • 내가 봐도 나는 얕은 지식으로 다른사람들에게 설명하려는 습관이 있다. 잘못된 지식의 전파는 모두가 잘못되는 길이기 때문이 이러한 습관을 지양해야한다.
  • 잘못된 결론에 도달하지 않기 위해서는 그만큼 deep하게 학습하여야 한다. 팀장님께서 항상 배운 것은 글로 정리하는 습관을 들여라고 지도해주셨고,
  • 이는 내가 향후 기술에 대해 이야기 할때 조금 더 매끄럽게 나의 의견을 주장할 수 있는 밑거름이 되었다.
  • 기술에 대해 이야기 하려 하니 모르는 것이 많아 발표능력이 모자란 것도 사실이다. 항상 머리 속에서 생각을 하고 말을 하도록 하자.
  • 한참 모자라지만, 뒤로 퇴보할 것이 개선될 여지만 있기 때문에 충분히 많이 성장할거라 생각한다..

 

5. 2020 목표 (와..... 많다... 파이팅..)

  • dynamodbguide.com에 번역 contritube를 하기로 했다. (승인을 받았으며, 2020/2/1부터 천천히 해나갈 예정이다)
  • Serverless APP에 대해 사상부터 개발까지 deep하게 이해를 하여 나 없으면 프로젝트가 진행 되지 않을 정도의 실력을 지녀야 한다.
  • Pending된 Kubernetes와 docker에 대해 학습을 재개하여 현재 서비스를 일시 중지한 냠냠챗봇에 대해 이 기술을 적용하여 사용자 중심의 서비스로 재오픈
  • AWS Solution Architect Professional과 AWS DEVOPS자격증 취득
  • NOSQL과 RDB의 deep dive

'회고' 카테고리의 다른 글

20200624 회고 - 배치 프로세스 및 데이터베이스  (0) 2020.07.28
2019 회고  (2) 2020.02.01
20191126 회고 - 프로그래밍 패턴  (0) 2019.11.26
  1. 달고냥이 2020.02.12 22:43

    일은 삶의 도구로 잘 활용하는 것이 중요합니다
    일이 삶의 목적이 되지 않기를..

  2. LOL전사 2020.04.16 10:34

    잘 보고갑니다~

1. 예외 처리

 

def S3ObjectDownload(bucket, key): 
 
    # S3에 자원에 접근 할 access key와 secret key를 입력합니다. 
    s3 = boto3.resource('s3', aws_access_key_id='<YOUR ACCESS KEY>', aws_secret_access_key='YOUR_SECRET_KEY') 
 
    # 추출 된 key에서 경로 값을 제외한 순수 파일 명을 얻기 위한 작업 
    preOutPutName = key.split('/') 
    outPutName = preOutPutName[-1] 
 
 
    # 파라미터로 받은 값을 이용하여 가장 최근에 올린 파일을 EC2로 다운로드 
    s3.Bucket(bucket).download_file(key, outPutName) 
    print("Download successfully.") 
    ProduceKafkaMsg(outPutName) 

위 코드 중 4번째 줄에서 S3의 object에 접근하는 코드가 있다. 접근하고자 하는 object의 존재 유무에 따른 예외처리를 하고 있지 않기 때문에 개발하는데는 문제가 없지만, production 환경에서 클라이언트가 어떤 오류인지 알 수 없기 때문에 오류에 대해 계속 문의가 들어올 수 밖에 없다.

 

 

 

msgJson = json.loads(request.body.decode('utf-8')) 
    msgType=msgJson["Type"] 
 
    # AWS SNS notification 메시지를 구독 전, 구독 확인 요청 승인 
    if msgType=='SubscriptionConfirmation': 
        subscribeURL=msgJson["SubscribeURL"] 
        requests.get(subscribeURL) 
 
 
    # AWS SNS notification 구독 메시지에 대한 다음 작업의 trigger 
    elif msgType=="Notification": 
 
        msgJson = json.loads(request.body.decode('utf-8')) 
        message_data = json.loads(msgJson["Message"].decode('utf-8')) 
        record_data=message_data["Records"] 
        s3_data=None 
 
        for item in record_data: 
            s3_data=item["s3"] 
 
        # S3 Bucket 이름 추출 
        bucket = s3_data["bucket"] 
        name = bucket["name"] 

다음 코드는 Amazon SNS에서 전송한 message body를 json parsing하는 부분이다. json 중 “Type“ 속성을 기반으로 동작을 구분하는데, SubscriptionConfirm과 notification 이외의 값이 들어왔을 경우의 코드가 없기 때문에 좋지 않은 코드이다.

 

 

1.1. 개선 사항

  • try-catch를 이용한 예외 사항에 대한 분기 처리

  • 분기를 통해 어떤 에러인지 사용자가 알 수 있도록 response의 body에 에러 내용을 포함

  • 가능한 여러가지 상황에 대해서 코드로 표현 할 수 있도록 해야함.

 

 

 

 

 

2. 입력 값에 대한 validation

 

    producer = KafkaProducer(bootstrap_servers='<YOUR_BROKER_SERVER>:<YOUR_BROKER_PORT>', 
                             value_serializer=lambda v: json.dumps(v).encode('utf-8'), 
                             key_serializer=lambda v: json.dumps(v).encode('utf-8'), 
                             acks=1, max_request_size=1999999999, send_buffer_bytes=1999999999, 
                             buffer_memory=1999999999) 

위 코드는 Apache Kafka 라이브러리를 사용하여 broker에게 보낼 때, producer에 대한 설정 값을 명시하는 부분이다. Kafka에서 정해진 규격을 지키지 않으면 Kafka broker측에서 에러를 출력하여 적절하게 동작하지 않기 때문에 이에 대한 validation이 필요하다.

 

 

2.1. 개선 사항

  • 설정 값들의 허용 범위에 대한 validation 설정

 

 

 

 

 

3. Static 변수에 대한 처리

    producer = KafkaProducer(bootstrap_servers='<YOUR_BROKER_SERVER>:<YOUR_BROKER_PORT>', 
                             value_serializer=lambda v: json.dumps(v).encode('utf-8'), 
                             key_serializer=lambda v: json.dumps(v).encode('utf-8'), 
                             acks=1, max_request_size=1999999999, send_buffer_bytes=1999999999, 
                             buffer_memory=1999999999) 
s3 = boto3.resource('s3', aws_access_key_id='<YOUR ACCESS KEY>', aws_secret_access_key='YOUR_SECRET_KEY')

위 코드의 경우는 프로세스에 필요한 설정 값을 명시하는 부분이며, 사용자로부터 값을 입력 받기 때문에 코드 상에서 보기 쉬운 위치에 있어야 하며, 재사용이 쉬워야 한다.

 

 

 

 

3.1. 개선 사항

  • 전역적으로 사용되는 static한 변수에 대해 전역변수화 (잘 보이는 곳에 명시)

  • 또는 함수의 파라미터로 입력 받도록 설계

 

 

 

 

 

4. HttpRequest의 200 OK로 고정된 Response

def index(request): 
    # 1. Amazon SNS 서비스의 메시지 구독 요청과 메시지에 핸들링에 대한 샘플입니다. 
    # 2. SNS 메시지 형식과 사용법에 대해서는 Amazon SNS.md를 참조해주세요. 
    # 3. Kafka 라이브러리에 대한 자세한 설명은 본 KafkaProducer.py를 참조해주세요. 
    # 4. KafkaProducer.py는 python3 환경에서 제작된 샘플입니다. 
 
 
 
    msgJson = json.loads(request.body.decode('utf-8')) 
    msgType=msgJson["Type"] 
 
    # AWS SNS notification 메시지를 구독 전, 구독 확인 요청 승인 
    if msgType=='SubscriptionConfirmation': 
        subscribeURL=msgJson["SubscribeURL"] 
        requests.get(subscribeURL) 
 
 
    # AWS SNS notification 구독 메시지에 대한 다음 작업의 trigger 
    elif msgType=="Notification": 
 
        msgJson = json.loads(request.body.decode('utf-8')) 
        message_data = json.loads(msgJson["Message"].decode('utf-8')) 
        record_data=message_data["Records"] 
        s3_data=None 
 
        for item in record_data: 
            s3_data=item["s3"] 
 
        # S3 Bucket 이름 추출 
        bucket = s3_data["bucket"] 
        name = bucket["name"] 
 
 
        # 다운로드 하고자 하는 S3 Object 파일 이름을 추출 
        # 버킷 안에 존재하는 디렉토리 안에 파일이 존재할 경우, 
        # 추출 되는 파일 이름(key 값)은 <directory>/<file_name>의 형태로 출력됩니다. 
        s3object = s3_data["object"] 
        key = s3object["key"] 
 
 
        S3ObjectDownload(name, key)  
 
 
 
    return HttpResponse(request.body) 

각 상황에 맞게 적절한 응답을 취해주어야 하나, 이에 대한 예외 처리가 안되어 있고, django HttpResponsee의 기본 값인 200 OK만 계속 전달되고 있는 상황이다.

 

 

4.1. 개선 사항

  • 각 예외 상황에 맞는 에러코드와 body, header를 반환한다.

 

 

5. 주석

 

from __future__ import unicode_literals 
from django.views.decorators.csrf import csrf_exempt 
from django.shortcuts import render 
from django.http import HttpResponse 
import logging 
import json 
import requests 
import os 
from kafka import KafkaProducer 
import botocore 
import boto3 
 
 
logger = logging.getLogger(__name__) 
 
@csrf_exempt 
def index(request): 
    # 1. Amazon SNS 서비스의 메시지 구독 요청과 메시지에 핸들링에 대한 샘플입니다. 
    # 2. SNS 메시지 형식과 사용법에 대해서는 Amazon SNS.md를 참조해주세요. 
    # 3. Kafka 라이브러리에 대한 자세한 설명은 본 KafkaProducer.py를 참조해주세요. 
    # 4. KafkaProducer.py는 python3 환경에서 제작된 샘플입니다. 
 
 
 
    msgJson = json.loads(request.body.decode('utf-8')) 
    msgType=msgJson["Type"] 
 
    # AWS SNS notification 메시지를 구독 전, 구독 확인 요청 승인 
    if msgType=='SubscriptionConfirmation': 
        subscribeURL=msgJson["SubscribeURL"] 
        requests.get(subscribeURL) 

처음 보는 사람 입장에서 이 라이브러리가 어떤 라이브러리인지, 어떤 함수인지에 대한 정보가 없다. 읽는 사람으로 하여금 의문만 더 증폭시키는 코드가 된거같다.

 

 

5.1. 개선할 사항

  • 함수의 implement 여부, 파라미터, 웹 어플리케이션의 경우 Context 등에 대해 서술

  • 라이브러리가 어떤 라이브러리인지 설명이 필요

 

 

 

 

 

 

6. 코드의 모듈화(자바의 경우, 패키지화)

view.py에 모든 매서드와 기능들이 존재하기 때문에 어떤 부분에서 어떤 역할을 담당하는지 모호하다.

 

 

6.1. 개선할 사항

  • 워크 플로우 정의대로 비슷한 성격의 동작끼리 모듈화시켜 다른 파일로써 보관한다.

'회고' 카테고리의 다른 글

20200624 회고 - 배치 프로세스 및 데이터베이스  (0) 2020.07.28
2019 회고  (2) 2020.02.01
20191126 회고 - 프로그래밍 패턴  (0) 2019.11.26

+ Recent posts