현재 다노에서 다노샵 서버 개발을 하고 있는 대구올빼미입니다.(올빼미지만 밤 10시에 자는 건 비밀)

그리고 현재 다노샵 서버는 python을 베이스로 django 프레임워크를 활용하여 구성되어 있습니다.
많은 분들이 아시다시피 Django는 정말 좋은 기능들을 많이 제공해줍니다.

그중에서 초보 개발자에게 정말 좋은 것은 바로 ORM...!!
ORM은 객체(Object)와 관계(Relation)을 연결(Mapping)해주는 개념입니다(이라고 합니다).
객체와 관계형데이터베이스를 변형 및 연결해주는 작업이라고 말할 수 있습니다. 즉 개발하는 데 있어서
관계형 데이터베이스의 제약을 최대한 받지 않으면서, 객체를 클래스로 표현하는 것과 같이 관계형 데이터베이스를
객체처럼 쉽게 사용할 수 있도록 해줍니다.

# 30세 이상 멤버 정보 가져오기 in sql
select * from member where age >= 30;

# 30세 이상 멤버 정보 가져오기 in django orm
Member.objects.filter(age__gte=30)


여러 프레임워크에서도 이 ORM을 제공해 줍니다.

그렇다 보니, 실제 서비스에서도 보통 ORM을 통해서 개발을 많이 진행합니다.
하지만 이 ORM의 가장 무서운 점은 실제 안에서 어떤 쿼리문을 발생시켜서 저렇게 쉽게 쓸 수 있도록 해주는지
알 수 없다는 것입니다.

그동안 제가 했던 작업선에서는 평상시에는 전혀 성능적으로 이슈가 크게 되지 않다 보니
너무나도 편하게 ORM을 사용해왔습니다. (사실 ORM이 아직도 너무 좋습니다 흑....)

실제 ORM 쿼리가 어떻게 요청이 되는지, 사수분이 여러 번 이야기해주셨지만
직접적으로 와 닿지가 않아 그렇게 넘어갔습니다. (죄송합니다... 반성합니다ㅠㅠ)

그리고 그 문제는 생각보다 크리티컬 하게 일어났습니다.

최근에 다노샵에서는 옵션별 할인 프로모션과,
기존에 있었던 대용량 할인 정보를 함께 표시해주도록 하는 업무를 진행하게 되었습니다.
그리고 그동안에 잘 사용하던 ORM에서 드디어 문제를 일으키고 말았습니다.

기존에는 각각의 제품 단위로 프로모션 할인이 들어가게 되었고, 제품 단위로 할인율을 구해주면 되었지만,
이제는 제품 하위의 옵션 단위로, 그리고 해당 옵션이 대용량 할인을 하고 있는지 일일이 계산하여,
옵션별로 할인율을 표기해주어야 하였습니다.(그만큼 기존보다 더 많은 로직을 타야 했고, 제품에 대한 할인 정보이다 보니 
굉장히 많은 곳에서 해당 부분을 사용하고 있었습니다.)

해당 작업을 진행하며, DUE도 짧았지만 나름 정말 몰입하여(심지어 아침에 운동할 때도 이 생각만 하고), 구조를 짜고
코드를 구성하였습니다. (물론 ORM을 그동안도 잘 사용해왔기에, 성능은 신경 쓰지 않았습니다.)

그리고 그 결과는 아주 처참하게 나타났습니다. 

테스팅을 할 때는 잘 작동하는 것에 만족하면서 테스팅을 진행하였고(현재는 QA시스템이 도입되었습니다.),
빠듯하게 실제 live가 되었을 때 곧 웹사이트가 느려진 것을 알게 되었습니다. 그리고 다급하게 New Relic(New Relic은 SaaS 기반의 APM(Application Performance Management) 서비스를 제공하는 회사)을 살폈습니다.

Newrelic의 web transaction time( 사용자가 웹 페이지를 클릭했을 때, 그 페이지에 대한 응답을 받는데까지의 시간)을 보았을 때 기존보다 적게는 2배에서 많게는 5배까지 느려진 지표를 보여주었다.

신입 개발자를 등을 땀으로 범벅이게 만들었던 그날의 지표들...


이때서야 저는 ORM이 얼마나 무서운지 깨닫게 되었습니다. 그동안 ORM을 아무 생각 없이 사용하였던 나 자신에 대한 깊은 후회와,
반성이 밀려들어왔습니다. 다급하게 전체 캐시를 적용해주어 성능적인 이슈는 막고 있었지만, 이번 프로모션 기간이 끝나기 전에 해당 부분을 개선해주어야 하는 2번째 미션이 주어졌습니다. (즉 일을 2번 하게 되었습니다.)

그렇게 반성을 하며 해당 부분에 대한 리팩토링을 시작하였습니다.
크게 저는 4가지의 방법을 통해 성능을 개선시켰습니다. 지금부터 그 이야기를 해보려고 합니다.

먼저 ORM에서 실제 발생시키는 쿼리를 보지 못하면 절대 개선을 할 수 없습니다. 따라서 ORM에 대해서 잘 모를 때는 무조건 실행 쿼리문을 볼 수 있는 창을 따로 띄어놓고 개발하시기를 추천드립니다.(네... 물론 저는 그렇지 않았습니다. ㅠㅠ)

별첨 : ORM에서 실제 발생시키는 쿼리문 보기 (Django + mysql)

# mysql client에서
show processlist; # 현재 mysql에서 실행 중인 process 보기

show variables like 'general_log%'; # 현재 general_logs의 상태를 볼 수 있습니다.

set GLOBAL general_log='ON'; #general_log를 켤 때

set GLOBAL general_log='OFF'; #general_log를 끌 때

set GLOBAL gnenral_logs = 'ON'으로 하고 나면 어디에 로그를 쌓는지 보이게 됩니다.

general_log_file의 경로를 볼 수 있습니다.

이제 docker(아마 docker 환경에서 개발할 것으로 생각됩니다.)에서 해당 경로로 가서 해당 파일의 log를 tail로 보게 되면
실행되는 쿼리들을 볼 수 있습니다. 

# docker 환경이 아니시거나 혹시 다른 방법으로는 

django-logging : docs.djangoproject.com/en/3.0/topics/logging/
print문으로 바로 찍어 보기 : stackoverflow.com/questions/3748295/getting-the-sql-from-a-django-queryset

와 같은 방법을 이용해보시면 좋을 것 같습니다.

첫 째 : 그 동안 크게 생각하고 있지 않던 select_related에 대해 다시 공부하여 적용해주었습니다. (1+N쿼리문제 해결)

관계형 데이터베이스의 장점은 Foreign Key를 묶을 수 있고 해당 Foreign key로 다른 테이블에도 접근할 수 있다는 것입니다.
하지만 이렇게 Foregin Key로 묶인 데이터를 ORM에서 가지고 올 때 주의해야 할 부분이 있습니다.

간단한 예시를 들어보겠습니다.

class Member(models.Model):
	name = models.CharField(max_length=32)
    age = models.SmallIntegerField()
    detail = models.ForeignKey('MemberDetail', on_delete=SET_NULL)
    
	class Meta:
    	db_table = "member"
        
class MemberDetail(models.Model):
	address = models.CharField(max_length=32)
    
    class Meta:
    	db_table = "member_detail"

가장 쉽게 볼 수 있는 DB 모델을 하나 만들었습니다.
고객의 중요한 정보를 Member 모델에 넣고, 추가 정보들을 MemberDetail로 따로 저장하도록 모델을 구성하였습니다.

나이가 30살 이상인 고객들의 address를 가지고 오고 싶으면 어떻게 해야 할까요?

def get_member_address_list():
	result = []

    member_list = Member.objects.filter(age__gte=30)

    for member in member_list:
        member_address = member.detail.address
        result.append(member_address)

    return result

간단한 함수를 하나 만들고 실행시켜보면, 굉장히 놀랄 수도 있습니다. (물론 위의 ORM에 의해 발생하는 로그들을 보고 있을 때입니다.)
왜냐하면 member.detail.address 이 부분에서 Foregin key에 묶인 MemberDetail 정보를 가지고 오기 위해 새로운 쿼리를 발생시키는데, 
해당 for문을 돌 때마다 1번씩 발생시키기 때문입니다. (해당하는 고객이 1만 명이라면, 1만 번 발생하게 됩니다.)

숫자가 적을 때는 괜찮지만 숫자가 많아지게 되면 어마어마한 쿼리를 발생시키게 됩니다. (물론 컴퓨터는 굉장히 빠르기 때문에, 평소에는 잘 느끼지 못합니다.)

이것을 1+N 쿼리 문제라고 합니다.

이 부분을 개선해주기 위해서는 어떻게 해야 할까요? 바로 select_related를 써주어 처음 member_list를 query 해올 때, 함께 Foregin Key로 묶인 MemberDetail 정보를 함께 쿼리 해오는 것입니다. (filter를 적었다고 쿼리문이 발생하지 않습니다. 위에서는 for문을 돌아야 하는 시점에 해당 filter의 query가 실행되게 됩니다.)

해당 코드를 select_related를 통해 개선해보겠습니다.

def get_member_address_list():
	result = []

    member_list = Member.objects.select_related('detail').filter(age>30)

    for member in member_list:
        member_address = member.detail.address
        result.append(member_address)

    return result


ORM에서 filter를 해올 때 select_related를 통해 Foreign key 묶인 것을 한 번에 query를 해오면 해당 Foregin key가 필요할 때 굳이 또 query문이 생성되지 않아도 된다는 것입니다. 해당 함수를 실행시키면, 딱 1회 query 문이 실행되는 것을 볼 수 있습니다. Member에서 age가 30 초과인 사람들을 filter 해올 때 해당하는 사람들의 detail에 묶인 정보도 함께 query 해와 메모리에 저장해놓고 이후에 지속적으로 사용하게 됩니다. (select_related 이외에서 prefetch_related도 있으나 여기서는 다루지 않겠습니다.)

둘 째 : 매개변수를 넘겨줄 때 객체 형태로 넘겨주면 또 한 번의 쿼리를 줄일 수 있습니다.

보통 우리는 매개변수에 값을 전달해줄 때, 정수를 넘겨주는 경우가 많다. 예를 들면 member_id를 넘겨주는 것이다.
이것도 예시를 먼저 들어보면 좋을 것 같다.

이번에는 특정 멤버의 address를 가지고 오는 함수를 생성하였다.

class GetMemberInfo:
	
    # 특정 나이보다 큰 멤버들의 주소 정보를 가지고 오는 함수
    def get_member_address_list_from_age(age):
        result = []
        member_list = Member.objects.filter(age>30)

        for member in member_list:
            member_address = self.get_member_address(member.id)
            result.append(member_address)
	
    # 특정 멤버의 주소 정보를 가지고 오는 함수
    @staticmethod
    def get_member_address(member_id):
        member = Member.objects.get(id=member_id)
        member_address = member.detail.address
        return member_address

위의 get_member_address 함수를 보면 member_id라는 argument를 받아서, 그 안에서 member_id를 통해 member 정보를 다시 가지고 오고, 그리고 묶인 Foreign Key를 통해 그 멤버의 address를 뽑아내고 있습니다.

이렇게 되면 get_member_address 함수를 실행할 때마다 member에 대한 쿼리 1번과 member의 Foreign key로 묶인 detail(Member_Detail)에 대한 쿼리 1번 총 2번의 쿼리가 실행되게 됩니다. 이것을 1번으로 줄이는 것은 위의 select_related를 사용해주면 1번으로 줄일 수 있습니다. (member = Member.objects.select_related('detail').get(id=member_id)

그럼 1번은 꼭 어쩔 수 없이 실행되어야 하는 걸까요?

이 쿼리 1번도 아예 줄여 버릴 수 있습니다. 그 방법은 바로 argument로 member_id를 넘겨주는 대신 객체 자체를 넘겨주는 방법입니다.
위의 함수를 좀 수정해보도록 하겠습니다.

class GetMemberInfo:
	
    # 특정 나이보다 큰 멤버들의 주소 정보를 가지고 오는 함수
    def get_member_address_list_from_age(age):
        result = []
        member_list = Member.objects.select_related('detail').filter(age>30)

        for member in member_list:
            member_address = self.get_member_address(member)
            result.append(member_address)
	
    # 특정 멤버의 주소 정보를 가지고 오는 함수
    @staticmethod
    def get_member_address(member):
        member_address = member.detail.address
        return member_address

member_id를 argument로 넘겨주어서 다시 한번 get_member_address 함수에서 쿼리를 실행시키는 게 아니라,
애초에 argument로 쿼리 되어 뽑힌 객체 자체를 넘겨주는 것입니다.
그리고 추가적으로 member_list를 filter 해오는 단계에서 select_related를 통해 detail 정보를 함께 쿼리 해오게 되면, 객체 정보를 넘겨줄 때 Foreign key로 묶인 detail에 대한 정보도 함께 넘겨지게 되고, 놀랍게도 get_member_address 함수에서는 아예 쿼리가 발생하지 않게 됩니다. 그럼 쿼리는 member_list에 대한 쿼리 딱 1번으로 마무리할 수 있습니다. 

ORM을 사용하여 아무 생각 없이 코드를 작성하다 보면 쿼리의 효율이 굉장히 나빠질 수 있습니다. (또 한 번 반성합니다..)

셋 째 : 필요한 부분에는 캐시를 적용해 줍니다.

현재 다노에서는 Redis를 캐시 DB로 활용하고 있습니다.
필요한 적재적소의 부분에 캐시를 적용해주면 성능을 굉장히 높일 수 있습니다. 위의 과정들을 통해 성능을 개선하였다면, 더 비약적으로 
성능 개선을 위해서는 캐시 설정하는 것만큼 극단적인 효과를 보기가 쉽지 않습니다. 물론 캐시 역시 만들어서 특정 데이터베이스에 저장해 놓는 것이기 때문에 데이터에 접근하기 위한 시간이 소모되긴 하지만 적절한 위치에 캐시를 적용해 놓기만 하면 훨씬 개선된 성능을 볼 수 있습니다. 

이것도 예를 한번 들어보겠습니다.
제품에 대한 정보를 가지고 올 때, 단지 제품에 대한 정보만 가지고 오면 될까요? 사실 제품에는 묶여 있는 것들이 많습니다.
제품에 대한 정보뿐만 아니라 제품 옵션들 각각에 대한 정보, 그리고 배송에 관한 정보, 재고에 관한 정보, Q&A에 대한 정보, 후기에 대한 정보 등등 굉장히 다양한 데이터 테이블들이 묶여서 우리가 보는 제품에 대한 페이지를 구성합니다.

그럼 필연적으로 해당 테이블들을 모두 쿼리 해올 수밖에 없습니다. 하지만 사실 재고에 대한 정보를 제외하고는 제품에 대한 정보, 그리고 옵션에 대한 정보, 배송에 대한 정보 등은 한번 설정을 하고 나면 잘 바뀌지 않습니다.(물론 리뉴얼 등으로 바뀔 수도 있습니다.) 

이렇게 바뀌지 않는 정보들에 대한 것은 모두 모아 캐시로 설정해놓고, 재고에 대한 정보만을 쿼리 해와서 함께 붙여주기만 하면 성능이 훨씬 개선될 수 있습니다. 

여기서 캐시 설정에 주의할 점들이 있습니다.
먼저 해당 캐시의 시간을 적절하게 세팅해주는 것(너무 긴 시간을 설정해주면, 정보가 변경되었지만 반영이 안 되게 되고, 너무 짧은 시간 설정해주면 의미가 없게 됩니다.)
그리고 적절한 위치에 캐시를 설정해주고 (잘 변경되지 않는 데이터들), 또 특정한 순간(상품에 대한 정보가 바뀔 때 등)에 캐시를 깨 주는 로직을 추가해주어야 합니다. 

넷 째 : 원시 SQL문 적용해주기

django에서도 원시 SQL?을 사용하여 쿼리를 요청할 수 있습니다. 방법은 크게 2가지가 있습니다.
1. Manger.raw ()를 사용하여 원시 쿼리를 수행하고 모델 인스턴스를 반환하는 것
2. 모델 레이어를 완전히 파괴하고 사용자 정의 SQL을 직접 실행시키는 것

2가지의 방법을 통해 직접 SQL 문을 작성하여 실행시킬 수 있습니다. 현재 다노에서도 많은 데이터를 쿼리 해와야 할 때는
직접 SQL을 작성하여 DB에 connection을 뚫어서 직접 SQL을 요청하고 있습니다. 

다만 해당 부분은 ORM을 직접적으로 사용하는 방법이 아니므로, 이번 글에서는 자세히 다루지 않겠습니다.


이번에 잘못된 부분들에 대해서 리팩토링을 진행하면서는 크게 위의 1,2,3번의 요소들을 반영해 주었습니다.
그리고 실제 개선된 부분들에 대해서 이야기를 해보고자 합니다.

과연 그 결과는 ...!! 두둥

기존 결과 값과 비슷하거나 더 개선된 수치들도 존재하였다. (로직이 많이 추가된 것에 비해서 많은 부분 개선되었다고 판단하였습니다!)
다시 리팩토링을 하여서 비록 작업을 2번 하게 되었지만, 이번 기회를 통해 정말 정말 정말... ORM의 무서움을 직접 몸으로 겪게 되었고,
현재는 위의 규칙들을 잘 생각하며 코딩하고 있습니다..

ORM은 너무 편하고 좋지만, ORM의 실제 쿼리들이 어떻게 생성되는지 초창기에는 창을 함께 띄어놓고
보시면서 개발을 하시면 저와 같은 시행착오를 겪지 않으실 거라 생각하고,
개발하시는데도 많은 도움이 되실 거라 생각합니다.

읽어주셔서 감사합니다.

수업을 진행하다보면,
나도 부트캠프 출신이고 하다보니 꼭 부트캠프를 가야지만 개발자를 할 수 있냐?
라는 질문을 많이 듣는다.

가장 최근에 들어온 질문은 아래와 같다.

"안녕하세요. 튜터님! 수업 잘 듣고 있습니다.
수업을 듣다보니 부트캠프에 대해 알게 되었습니다.
근데 제 상황이 현재 부트캠프를 갈 수 있는 상황이 되지 않습니다.
혹시 그럼 개발자가 되기 어려울까요?

결론부터 이야기하면 "NO"이다.
사실 나는 부트캠프 6개월 과정 중 4개월만 하고 조기 졸업을 했다.

부트캠프를 하고 있는 도중에 혼자서 프로젝트 진행하고, 이력서 적고 해서
스스로 취업을 했다.

사실 수 많은 부트캠프들이 우리는 취업이 연계되어 있다.
그러니 우리 수업을 들으면 취업을 쉽게 할 수 있다. 이야기 한다.

너무 기본적으로 이야기하고 있는 내용이라, 그리고 실제적으로 업체들의 로고들을
사용해서 올리고 있으니, 누구라도 혹 할 수 밖에 없는 것 같다.(사실 처음 나조차도 그랬다.)

나와 같은 부트캠프를 나온 뒤에도 누군가는 취업을 하고, 누군가는 1년이 다 되가는 지금 시점에서도
취업을 못 했다. 결국 case by case 라는 이야기이다.

그리고 그 중에서도 내가 제일 빠르게 수업 중에 취업할 수 있었던 이유는
"부트캠프에 대한 기대"라고 생각한다.

사실 이 부분에서 제일 큰 차이가 나는데,
아직 취업하지 못한 친구들을 만나보면 다녔던 부트캠프에 대해서 굉장히 불만족하는 것을 알 수 있다.

"취업 다 시켜준다고 했는데, 시켜주지 않더라"
"포트폴리오도 다 만들어준다고 했었는데, 그렇지 않더라"

사실 그건 우리가 속은게 맞다.
포트폴리오도 다 만들어 주지 않고(사실 생각해보면 본인 포트폴리오를 다른 사람이 어떻게 만들어줄까..?)
취업도 다 시켜주지 않는다.(협력되어 있는 업체에 이력서를 보내주는 정도)

따라서 스스로 해야하는데, 이것을 곧이 곧대로 믿었다가는 큰 낭패를 볼 수 있다.

애초에 나는 부트캠프를 갈 때 여기에 대해서 큰 기대를 하지 않았다.
단지 하루에 12시간씩 공부하고 싶었는데, 그게 혼자해보니 쉽지 않았고,
내가 하루 12시간씩 개발 공부를 할 수 있는 환경만 조성해주면 된다.가 내가 부트캠프에서
바랬던 가장 큰 기대였다.

그리고 생각했던 것처럼 부트캠프는 해당 시간을 지킬 수 있도록 만들어주었다.

그렇게 나는 내 페이스에 맞추어서, 수업 중에 선생님께 쉬는 시간마다 물어가며
프로젝트를 진행했고, 포트폴리오도 주말에 혼자 만들었다.
그리고 만들어진 포트폴리오로, 이력서를 넣었다.

내가 갈 회사인데, 내가 직접 알아보고 넣어야지 생각하며 내가 가고 싶은 회사들을
골랐고, 내 스스로 면접들을 보고 취업했다.

포트폴리오만 잘 만들어지고, 나의 이력서만 있다면 사람인, 원티드, 잡코리아, 로켓펀치 등
스스로 너무나도 좋은 회사들에 직접 지원할 수 있다.(이걸 부트캠프에서 굳이 해줄 필요가 있나?)

결국 부트캠프에서 해당 채용 업체에 이력서를 보내주면, 해당 업체에서도 잘 된 이력서를 보고
사람을 뽑는다. 이 말은 즉 스스로 잘해야 된다는 것이다.

사실 너무나 당연한 이야기인데, 부트캠프에서 이것을 너무 크게 광고를 해서,
너무 큰 기대를 가지고 부트캠프를 가게 되면, 후회할 확률이 높다.

그리고 스스로는 준비를 하지 않게 된다. 왜냐하면 나는 비용을 지불했고,
부트캠프에서 다 해준다는 광고를 보고 왔으니깐!!

나는 이것은 별로 좋지 못하다고 생각한다.

따라서 혼자서 공부만 잘 할 수 있으면, 
절대 부트캠프를 꼭 가야하는 이유는 없다.

스스로도 충분히 할 수 있다. 다만 나처럼 혼자 공부하기 힘든 사람들은
국비지원 학원을 가든 부트캠프를 가던 가야지 개발자로 더 빠르게 입문할 수 있는 확률이 높아진다.

많은 기대를 하지 않고,
본인이 원하는 것을 명확히 하고,
본인 스스로 생각해서 결정하면
다 할 수 있다. 못 할 것은 없다.

처음 개발자를 시작할 때 나조차도 이곳 저곳 알아보고 상담받고 2달 넘게 방황했기에 ㅠㅠ.
이렇게 글을 적는다.

다른 사람들은 나와 같은 시행착오를 겪지 않았으면 좋겠다는 마음이다.
(혹시 질문이 있으신 분들은 아래 수업에 오픈 채팅에서 해주시면 상담도 도와드릴 수 있습니다.)

개발자를 하기로 마음 먹었으면, 명확한 방향을 잡고 하루라도 빨리 취업하는게 정답이다.

www.inflearn.com/course/개발자-취업-입문-개론?inst=b3611dbc

 

비전공자를 위한 개발자 취업 개론 - 인프런

개발자 취업 입문 개론 수업입니다. 평생 한 직업만 하실 게 아니라면, 꼭 한번은 개발자를 도전해 보시길 추천드려요! 비전공자 혹은 현재 다른 업무를 하더라도, 상관없습니다. "쌀 팔다 개발

www.inflearn.com

 

 

[드디어...!!!!!!!!!!!!! 추가 소식 전달!]

안녕하세요 쌀 팔다 개발자하고 있는
김병욱입니다! 

저 역시도 29살의 나이에,
처음으로 개발이라는 것을 배우면서
무엇을 해야 할지 몰라 많은 시행착오를 겪었습니다.

그리고 현재는 잘 성장하여,
3년차 백엔드 개발자로 일하고 있습니다.
새롭게 개발 시작하시는 분들이 저와 같은 시행착오를

겪지 않았으면 좋겠다는 생각으로 개발자가 된 이후로
지속적으로 강의를 해왔었는데, 항상 시간이 부족해서
아쉬움이 가득이었습니다.

그래서, 정말 큰 맘 먹고 올해 1월부터 책을 적기 시작했습니다.
회사 출근 전 퇴근 후 최대한 시간을 내서(피,땀, 눈물 ㅠㅠ), 제가 알고 있는,
그리고 부족한 것은 주변 개발자분들에게 물어가며
정말 열심히 적었습니다!

그리고 10개월이 지난, 이제서야 이 책이 정말 곧 빛을 보려고 합니다.
개발 시작하시는 분들이 정말 꼭 읽고 시작했으면 좋겠다는 생각에
용기내어 이렇게 글을 적습니다. 최소 2개월은 save하실 수 있으실거에요!
(책 팔아서 돈을 번다는 것은 정말... 너무 어려운 일이고, 그러고 싶지도 않습니다.)

정말 이 책이 정말 개발 시작하려는 분들에게는
조금이라도 도움이 되었으면 좋겠습니다.
그것이면, 전 정말 만족할 것 같습니다.

현재 텀블벅에서 펀딩 진행 중입니다.
주변에 개발자 하시고 싶어하시는 분들이 있다면 많이 추천해주세요!
https://tumblbug.com/tomorrow_programmer?ref=discover

 

취업까지 로켓배송! 개발자 취업 가이드 [오늘부터 개발자]

개발자 취업까지 수백시간 줄여줄 비전공자 취업 입문 개론 [오늘부터 개발자]

www.tumblbug.com

감사합니다!

 

말 그대로 정말 초보자를 위한 REST API 설명이다.

그 동안 개발을 하면서 RESTFUL하게 코드를 설계하였나? 라고 하면...
딱히 대답할 말이 없었다.

REST가 무엇인지 몰랐고, 또 이게 왜 필요한지도 잘 몰랐다.
사실 그 동안 REST API에 대해서 검색도 많이 해보고 많이 찾아보기도 했는데,
그 당시의 나의 수준에서 이해하기 어려웠는지, 이해가 잘 되거나 와닿지 않았다.

그러다가 정말 초보자 수준에서 이렇게 설명하면 좋을 것 같다는 생각이 들어
내가 느낀 대로 REST API를 정리해보려고 한다.

일단 REST API를 알려고 하면 API를 알아야 한다.

API는 Application Programming Interface의 줄인 말이다.
Application들이 서로 통신할 수 있도록 도와준다고 생각하면 될 것 같은데,
우리는 프론트와 서버가 통신하기 위해 API를 활용한다.

예를 들어 보자.


이런 상황은 얼마든지 발생할 수 있다. 또 반대로 금은방에 가서 돼지 불백을 주문하는 불상사가
생길 수 있다. 사실 이런 상황은 프론트와 서버와의 통신간에 더 많이 발생할 수 있다.

그러기 위해 서로 약속을 해야 한다. 이런 인터페이스를 사용하겠다는..
그게 API다. 다시 API의 보면 Application Programming Interface이다.

Programming Interface 개발을 위한 인터페이스인데, Application 간의 통신을 위한 인터페이스

자 여기서 한번 더 들어가보자. 이제 프론트와 서버간의 통신을 위한 인터페이스는 정했고,

그럼 REST API란 무엇일까?

아래의 상황을 보자.

음식점에 왔고, 손님은 이번에는 금값을 묻거나 그런 것이 아니라 정말 "음식"이라는 것을 잘 주문했다.
이 상황을 보고 나면 사실 인터페이스 뿐만 아니라 또 한번의 "약속"이 필요하다는 것을 느낄 것이다.

즉 어떻게 요청 할 것인지에 대한 약속. 그것을 위해 생겨난 것이 REST API다.(와 길게 왔다...)

REST API는 서버와 프론트 간에 어떻게 요청하고, 어떻게 전달 받을지에 대한 "약속" (효율을 높이기 위해!) 이라고 하면 좋겠다.

일단 REST API에 대한 정의를 보자.

참고 : mangkyu.tistory.com/46

일단 CRUD를 알아야 할텐데 Create, Read, Update, Delete의 줄인 말이다.
사실 우리가 통신할 때 하는 행동은 대부분 위의 4가지 이다. 이거는 조금만 고민해 보면 아는데, 게시판에 글을 적는다는 것은 Create하는 행위 일 것이고, 다른 게시판으로 이동하는 것은 Read하는 행위, 게시글을 수정하는 것은 Update, 게시글을 지우는 것은 Delete이다.

그럼 이 CRUD한 요청을 보내기 위한 "약속"이라고 볼 수 있는데 REST API에는 3가지의 구성 요소가 있다.

URI : product/32(id) => 서버는 요청에 대한 고유 id의 값을 가지고 있고, 해당 id로 요청을 보낸다는 것 1가지

METHOD : GET/POST/PUT/DELETE => 여기 드디어 나왔다. GET과 POST!

Representation of Resource : Json, XML, TEXT => Json은 여기에 숨어 있었다. JSON!

API의 효율적 통신을 위해 다시 한번 약속을 한 것이 REST API라고 했다. 이제 하나씩 보자.

URI에 대한 것은 조금만 눈치가 빠른 사람이라면 알 수 있다.
아래의 네이버 영화 페이지의 URL이다.
https://movie.naver.com/movie/bi/mi/basic.nhn?code=180378

여기서 제일 위의 code=180378 이라고 나오는데 여기 코드 번호를 하나만 바꾸면 또 새로운 영화로 바뀌면서 영화 정보가 나오는 것을 볼 수 있다.

그럼 다시 REST API 3가지 구성 요소 중 URI에 대한 부분을 보자.
"서버는 요청에 대한 고유 id의 값을 가지고 있고, 해당 id로 요청을 보낸다"

이제 이 말이 조금은 이해가 될 것이다. 해당 API(영화 정보를 조회하는 api는 https://movie.naver.com/movie/bi/mi/basic.nhn?

여기 제일 위에 code= 와 같이 조회할 영화 정보를 넣는다는 것이 REST API 약속 중 한 가지이다.

그럼 2번 째 드디어 나온.. 우리가 너무 자주쓰고 있는 GET과 POST가 나왔다.

"정보를 요청할 때는 GET 요청을 보내고,
정보를 입력할 때는 POST로 요청을 보낸다."
이렇게 약속을 해놓은 것이다. 이렇게 되면 서버에서는 프론트에서 GET 요청이 올 때는 DB에서 데이터를 조회해서
보내주는 로직으로 구성하면 되고, POST 요청이 오면 작성에 대한 로직을 구성하면 되기 때문에 굉장히 효율이 높아지게 된다.

이렇듯 REST API는 API 통신이 원활하기 위해 또 한번 약속을 해놓은 것이라고 볼 수 있다.

실제 REST에 대한 백과사전을 찾아보면

REST(REpresentational State Transfer) :

“Representational State Transfer라는 용어의 약자로서 2000년도에 로이 필딩 (Roy Fielding)의 박사학위 논문에서 최초로 소개됨. 로이 필딩은 HTTP의 주요 저자 중 한 사람으로 그 당시 웹(HTTP) 설계의 우수성에 비해 제대로 사용되어지지 못하는 모습에 안타까워하며 웹의 장점을 최대한 활용할 수 있는 아키텍처로써 REST를 발표”

와 같은 글을 볼 수 있다.

그럼 이제 마지막 Representation of Resource : Json, XML, TEXT 을 보자.

서버에서 프론트로 값을 줄 때 항상 JSON 형태로 전달을 하고 있어서 그냥 그런가 보다 생각을 했었는데,
이것도 REST API의 일부였다..!! 

서버에서 프론트로 결과 값을 줄 때 JSON/XML/TEXT 등의 값으로 반환한다는 것을 정의 해놓은 것인데,
이렇게 되면 프론트엔드 쪽에서도 어떤 형태로 값이 넘어올지 미리 예상할 수 있어 이렇게 맞추어 코딩을 하면 된다.

현재는 JSON 위주로 쓰이고 있어서 우리는 보통 서버에서 프론트로 반환 값을 줄 때 JSON형태로 전달하고 있다.
JSON은 key와 value로 이루어진 값의 모임인데 간단하게 형태를 예시로 들어보면 아래와 같다.

 

와 이제 정말 다 설명했다.

정리를 다시 해보자면

API 사용하기 위해서는 약속이 다시 한번 필요했고! 이 약속을 효율적으로 한 것이 REST API라는 것.

RESTFUL하게 구성하였냐는 이 REST의 구성요소 들을 잘 지키면서 API를 작성했는지에 대한 것이다.

나처럼 헤메고 있는 누군가에게는 도움이 좀 되었으면 좋겠다.


ps . 저와 같이 초보자를 위해 정리한 것이라 다소 틀린 부분이 있을 수 있습니다.

'서버' 카테고리의 다른 글

Graphql이란  (0) 2020.09.26
nslookup 이란  (0) 2020.08.31
Docker란(도커란)  (0) 2020.06.02
NAS란(나스란)  (0) 2020.06.01
MOUNT란  (0) 2020.06.01

파일을 줄별로 저장할 때 - 예시

from openpyxl import *
from openpyxl.styles import Font, Alignmnet
from openpyxl.utils import get_column_letter


# excel sheet 시작하기
wb = Workbook()

# sheet 만들기 (기본적으로 1개 제공 추가 만들기)
worksheet = wb.create_sheet(sheetname)

# sheet 1개 더 만들기
worksheet1 = wb.create_sheet(sheetname1)

# field 이름 설정하기
field_name = ['이름', '나이', '주소', '연락처']

# field width 설정하기
field_width = ['10', '5', '30', '20']

# 특정 넣을 값을 만드는 로직 추가
member_list = Member.objects.all()


# 먼저 field 설정해주기 

for i, head in enumerate(field_name):
    # cell(1)을 해줘야지 가장 위에 줄에 입력해준다.
    # 해당 필드의 width 설정해주기
    worksheet.column_dimensions[get_column_letter(i + 1)].width = field_width[i]
    # 해당 필드의 값 주기
    worksheet.cell(1, i + 1).value = head
    # 폰트 설정해주기
    worksheet.cell(1, i + 1).font = Font(name='맑은 고딕', size=13, bold=True)
    # 폰트 위치 설정해주기 (정중앙정렬)
    worksheet.cell(1, i + 1).alignment = Alignment(horizontal='center', vertical='center')


# 해당 field들에게 값 주기
for i, data in enumerate(member_list):
    # i + 2를 해줘야지 2번째 줄부터 입력해준다.
    worksheet.cell(i + 2, 1).value = data.name
    worksheet.cell(i + 2, 2).value = data.age
    worksheet.cell(i + 2, 3).value = data.address
    worksheet.cell(i + 2, 4).value = data.phone


# 가장 기본적으로 셋팅되어 있는 sheet 삭제
wb.remove(wb['Sheet'])

# 저장하기
wb.save('member_name.xlsx')

 

파일을 읽어서 처리할 때 - 예시

 

import openpyxl


def open_pyxl():
    not_matching_membmer = []
    multiple_member = []
    what_error = []
    
    coupon = Coupon.objects.get(id=636)
    success_count = 0


    wb = openpyxl.load_workbook('account.xlsx')  # 해당 파일 불러오기
    ws = wb.active # 활성화 시트가지져오기
    wr = ws.rows # 열별로 가져오기 한줄 한줄 가져오기
    for i in wr:
        try:
            account = i[0].value (해당 줄의 첫 번째 행의 값 가져오기)
            member = Member.objects.get(account=account)
            coupon.issue([member])
            print('coupon _issue success : ', member.id, ' id: ', member.account)
            success_count += 1
        except Member.DoesNotExist:
            not_matching_membmer.append(account)
        except Member.MultipleObjectsReturned:
            multiple_member.append(account)
        except Exception as e:
            dict = {}
            dict[account] = str(e)
            what_error.append(dict)


    print('성공 갯수 :', success_count, '매칭 맴버 x : ', not_matching_membmer, '중복 멤버 :',
          multiple_member, '알 수 없는 에러 :', what_error)

 

[비슷한 질문을 해주시는 분들이 많아서 많이 해주시는 질문들을 모아보려고 합니다.]

오늘은 인프런 오픈 채팅을 통해 이런 질문을 받았다.

질문은 아래와 같다.

"안녕하세요. 수업 잘 들었습니다.
수업을 듣고 질문이 있어서 연락드립니다.

사실 수업을 듣고 너무 개발자를 해보고 싶다는
생각도 들고, 방향 설정도 너무 잘해주셔서 도움이 많이 되었습니다.

다만 걱정되는게
저 개발자가 잘 맞을지 모르겠습니다.
사실 예전부터 개발자를 해보고 싶었는데,
이래 저래 조금 공부하다가 또 다시 원래 하던 일로 돌아가고...
이러기를 벌써 3번은 반복한 것 같습니다.

개발자에 도전해보고 싶은데,
또 전처럼 포기할까봐 걱정됩니다.

튜터님이시라면 어떻게 하셨을까요?"

이 질문을 듣고, 답변을 드리기가 쉽지 않았다.
왜냐하면 사람마다 모두 상황이 다르고, 본인 성격도 모두 다르기 때문이다.

다만 여기에서 확실한 것은 있다.
질문 주신 분은 개발자를 하고 싶어하시고,
그러셨기에 내 수업을 들었을 것이다.

사실 사람들은 질문을 할 때 어느정도는 정답을 가지고 있다.

아마 질문 주신 분도 정답을 가지고 계셨을 것이다.
다만 나의 답변을 통해 확신을 얻고 싶으셨을 것 같다.

사실 개발자를 하고 나서 느낀 것인데,
개발자가 잘 맞을지 고민을 하는 것은 아무것도 의미가 없다.

또 개발자가 잘 맞지 않는다고 해서, 
직업으로 개발자를 못할 이유도 없다.

우리가 가진 직업을 모두 잘 맞아서 하고 있지 않듯이,
개발자도 사실 똑같다.

누군가는 정말 잘 맞을 수도 있고,
누군가는 그저 그럴 수도 있고,
누군가는 정말 잘 안 맞을 수도 있다.

정말 잘 맞는다면 너무 좋겠지만,
그렇지 않는다고 하더라도 직업으로 개발자는 누구든지 할 수 있다.

그리고 잘 맞는지 안 맞는지는 정말 ...
꼭 해봐야 안다. 해보지 않고 고민만 해서는 정답이 없다.

그래서 너무 큰 부담을 가지지 마시고,
직업으로서 개발을 시작해보라고 이야기드렸다.

그리고 직접 경험을 해보시고, 판단을 하시는게
좋을 것 같다고 이야기드렸다.

다만 나의 주관적인 의견을 좀 더해보자면,
개발자가 잘 맞던 맞지 않던,
요즘 같이 100세 인생을 살아가며 한번쯤은 꼭 직업으로
개발자라는 직업을 해보기를 추천드린다.

다만 해보기로 마음 먹었으면,
최대한 빨리 정말 최대한 빨리 개발자로 입문하기를 추천드린다.

시간이 길어지만 길어질수록 오히려 포기할 확률도 높아지고,
쉽지 않아지게 되는 것 같다. 빨리 개발자가 되면 될수록 이점도 훨씬 많다.

쌀 팔다 6개월만에 개발자 된 비법이 궁금하다면,
www.inflearn.com/course/개발자-취업-입문-개론?inst=b3611dbc

 

비전공자를 위한 개발자 취업 개론 - 인프런

개발자 취업 입문 개론 수업입니다. 평생 한 직업만 하실 게 아니라면, 꼭 한번은 개발자를 도전해 보시길 추천드려요! 비전공자 혹은 현재 다른 업무를 하더라도, 상관없습니다. "쌀 팔다 개발

www.inflearn.com

 


[드디어...!!!!!!!!!!!!! 추가 소식 전달!]

안녕하세요 쌀 팔다 개발자하고 있는
김병욱입니다! 

저 역시도 29살의 나이에,
처음으로 개발이라는 것을 배우면서
무엇을 해야 할지 몰라 많은 시행착오를 겪었습니다.

그리고 현재는 잘 성장하여,
3년차 백엔드 개발자로 일하고 있습니다.
새롭게 개발 시작하시는 분들이 저와 같은 시행착오를

겪지 않았으면 좋겠다는 생각으로 개발자가 된 이후로
지속적으로 강의를 해왔었는데, 항상 시간이 부족해서
아쉬움이 가득이었습니다.

그래서, 정말 큰 맘 먹고 올해 1월부터 책을 적기 시작했습니다.
회사 출근 전 퇴근 후 최대한 시간을 내서(피,땀, 눈물 ㅠㅠ), 제가 알고 있는,
그리고 부족한 것은 주변 개발자분들에게 물어가며
정말 열심히 적었습니다!

그리고 10개월이 지난, 이제서야 이 책이 정말 곧 빛을 보려고 합니다.
개발 시작하시는 분들이 정말 꼭 읽고 시작했으면 좋겠다는 생각에
용기내어 이렇게 글을 적습니다. 최소 2개월은 save하실 수 있으실거에요!
(책 팔아서 돈을 번다는 것은 정말... 너무 어려운 일이고, 그러고 싶지도 않습니다.)

정말 이 책이 정말 개발 시작하려는 분들에게는
조금이라도 도움이 되었으면 좋겠습니다.
그것이면, 전 정말 만족할 것 같습니다.

현재 텀블벅에서 펀딩 진행 중입니다.
주변에 개발자 하시고 싶어하시는 분들이 있다면 많이 추천해주세요!
https://tumblbug.com/tomorrow_programmer?ref=discover

 

취업까지 로켓배송! 개발자 취업 가이드 [오늘부터 개발자]

개발자 취업까지 수백시간 줄여줄 비전공자 취업 입문 개론 [오늘부터 개발자]

www.tumblbug.com

감사합니다!

 

국비 지원 학원을 가면 무조건 SI업체로 가는 것이냐고 질문을 주시는 분들이 많다.
(벌써 해당 질문한 10번은 넘게 들은 것 같다.)

또 국비 지원 학원과 부트캠프 중에 어떤 곳을 가야지 좋을지에 대한 질문도
엄청 많이 들어온다. (이것은 다음에 따로 다루도록 해야할 것 같다.)

먼저 국비 지원학원을 가면, 무조건 SI업체 확정일까?

최근에 이런 질문이 있었다.


먼저 수업을 즐겁게 들어주셨다고 하셔서 ! 너무 감사했다 ㅠㅠ :)

또 OKKY에도 보면 국비지원 학원에 대해 비난 하는 글들이 많다.

나는 따로 국비지원 학원을 다니지 못해, 직접 경험해 보지 못했지만
탈잉 오프라인 수업을 진행하면서 국비 지원 학원을 다니신 분들이 많았고,
또 내가 다녔던 부트캠프에도 국비지원 학원을 다니고 온 친구들이 있었다.

즉 직접 경험을 하진 못했지만, 간접적으로 경험할 수 있었다.

일단 국비지원 학원을 가면 SI업체를 많이 가게 되는 이유는 있는 것 같다.
(제 3자의 입장에서 내가 느낀 것을 적어보고자 한다.)

먼저, 포트폴리오에 많이 신경쓰지 않는다.
사실 신입 지원자에게는 포트폴리오가 굉장히 중요하다. 
잘 꾸민 포트폴리오는 열자식 부럽지 않다. 왜냐하면 신입일 때는, 경력도 없고 
내가 얼마나 개발을 잘하는지 나타내기도 어렵다.

그렇기에 회사 지원에 첫 인상이 되어줄 포트폴리오는 굉장히 중요하다.

내가 취업할 때 만들었던 포트폴리오를 첨부하면 조금이라도 도움이될까 해서 첨부해본다.(http://deaguowl.github.io)
이 부분도 할 이야기 많은데, 사실 부트캠프라고 해서 포트폴리오를 다 만들어주지 않는다.

이 말은 즉, 본인이 직접 포트폴리오를 만들고 준비해야 한다는 것이다.( 준비한 자에게 기회가 많이 찾아올 수 밖에 없다.)
그렇게 잘 만들어진 포트폴리오 한개만 있어도, 좋은 회사에 면접을 볼 수 있는 기회가 
몇배는 더 늘어나게 된다.

그리고 두 번째, 국비지원 학원에서 추천해주는 회사가 대부분 SI업체라는게 문제다.
사실 지원자가 좀 더 관심을 가지고 찾아보면 너무나도 많은 회사들이 있다. 그리고 그 중에는 좋은 회사들도 많다.

하지만 사실 공부하다보면 이리 치이고 저리 치이고, 어떤게 좋은 회사인지에 대한 판단도 잘 없다.
그래서 준비를 잘 해놓고도, 생각보다 지원도 해보지 않고 그냥 학원에서, 주변에서 추천해주는 회사에
또는 먼저 합격한 회사에 가는 경우가 많다.

그렇기에 본인만의 기준을 세우는 것이 제일 중요하다.
"나 최소 연봉 얼마 이상은 갈 거야. 나는 개발 문화가 제일 중요해. 사수는 꼭 있었으면 좋겠어' 등등

본인만의 기준을 세우고, 회사를 "직접" 찾아 나서야 한다.

그러지도 않아놓고, 학원에서 추천해준 SI업체에 가게 되었다고 불평하면 안된다.

사실 국비 지원 학원에서는 취업율에 관심이 많지, 수강생이 어떤 회사를 갔는지 크게 관심이 없다.

결국 중요한 것은 본인이 얼마나 마음을 가지고 찾아보는 가에 달려있다.

회사에 대해서 조금만 관심을 가지고 찾아보면,

로켓펀치 / 원티드 / 잡플래닛 등을 통해 회사에 대해서 어느정도 알아볼 수 있다.


한 번 들어가고 나면 최소 1년은 일할 회사인데,
본인이 직접 선택하고 고르기를 추천한다!!

포트폴리오 만들기 / 좋은 회사 고르기는 인프런 강의 내용 속에 담아놓았다 (진심으로 개발자를 준비 한다면 꼭 듣고 준비를 했으면 한다 ㅠㅠ 그럼 최소 2달은 save할 수 있다고 확신...!)
=>www.inflearn.com/course/개발자-취업-입문-개론?inst=b3611dbc

 

비전공자를 위한 개발자 취업 개론 - 인프런

개발자 취업 입문 개론 수업입니다. 평생 한 직업만 하실 게 아니라면, 꼭 한번은 개발자를 도전해 보시길 추천드려요! 비전공자 혹은 현재 다른 업무를 하더라도, 상관없습니다. "쌀 팔다 개발

www.inflearn.com

 

 

 

[드디어...!!!!!!!!!!!!! 추가 소식 전달!]

안녕하세요 쌀 팔다 개발자하고 있는
김병욱입니다! 

저 역시도 29살의 나이에,
처음으로 개발이라는 것을 배우면서
무엇을 해야 할지 몰라 많은 시행착오를 겪었습니다.

그리고 현재는 잘 성장하여,
3년차 백엔드 개발자로 일하고 있습니다.
새롭게 개발 시작하시는 분들이 저와 같은 시행착오를

겪지 않았으면 좋겠다는 생각으로 개발자가 된 이후로
지속적으로 강의를 해왔었는데, 항상 시간이 부족해서
아쉬움이 가득이었습니다.

그래서, 정말 큰 맘 먹고 올해 1월부터 책을 적기 시작했습니다.
회사 출근 전 퇴근 후 최대한 시간을 내서(피,땀, 눈물 ㅠㅠ), 제가 알고 있는,
그리고 부족한 것은 주변 개발자분들에게 물어가며
정말 열심히 적었습니다!

그리고 10개월이 지난, 이제서야 이 책이 정말 곧 빛을 보려고 합니다.
개발 시작하시는 분들이 정말 꼭 읽고 시작했으면 좋겠다는 생각에
용기내어 이렇게 글을 적습니다. 최소 2개월은 save하실 수 있으실거에요!
(책 팔아서 돈을 번다는 것은 정말... 너무 어려운 일이고, 그러고 싶지도 않습니다.)

정말 이 책이 정말 개발 시작하려는 분들에게는
조금이라도 도움이 되었으면 좋겠습니다.
그것이면, 전 정말 만족할 것 같습니다.

현재 텀블벅에서 펀딩 진행 중입니다.
주변에 개발자 하시고 싶어하시는 분들이 있다면 많이 추천해주세요!
https://tumblbug.com/tomorrow_programmer?ref=discover

 

취업까지 로켓배송! 개발자 취업 가이드 [오늘부터 개발자]

개발자 취업까지 수백시간 줄여줄 비전공자 취업 입문 개론 [오늘부터 개발자]

www.tumblbug.com

감사합니다!

 

python에서 비교 연산자를 할 때 가장 많이 쓰는 것은 == 이다.

또 is를 쓸 수도 있다.


하지만 이번에 아무 생각 없이 is를 사용하였는데, 그냥 넘어가는 케이스가 있어서 당황하게 되었다.

예를 들어 설명해보면

if ga_uid is not 'anonymous':
    print('here')

이런 경우 ga_uid에 분명 'anonymous'가 들어왔는데도

그냥 print문이 찍히게 되었다.

 

그래서 알아보니 is에 이런 설명이 있었다.

1. 'is'는 비교 연산자가 맞다. 하지만 중요한 건 변수의 값을 비교하는게 아니라 레퍼런스(C식으로 설명하자면 변수의 포인터)를 비교하는 연산자다.

2. Python의 변수는 내부적으로 데이터 그 자체를 가리키는게 아니라 인스턴스 포인터(값이 저장되어 있는 메모리의 주소, 즉 레퍼런스)를 가리킨다. 물론 C언어가 아니기 때문에 변수를 포인터처럼 엑세스 할 수는 없다.

 

이 말은 즉 memory에 올라간 주소를 체크한다는 것인데, 파이썬에서는 자주 쓰이는 것은 미리  memory에 올려놓고 사용한다.

 

따라서

a is a
True


라고 나온다. a는 파이썬에서 미리 memory에 올려놓았었기 떄문이다.

하지만 anonymous는 새롭게 memory에 올라가게 되고 이런 경우 ga_uid가 'anonymous'라고 해도 해당 부분에서 걸리지 못하게 되는 것이다.

 

따라서 is 커맨드는 되도록이면 None, True, False와 같은 것을 비교할 때만 하는게 좋을 것 같다.

 

그에 비해 ==은 정말 같은지를 비교하는 것이라 되도록 ==를 사용하는게 좋을 것 같다.

 

참고 

[[Python] 'is' 커맨드 - Seorenn SIGSEGV](http://seorenn.blogspot.com/2011/04/python-is.html)

[operators - Python != operation vs "is not" - Stack Overflow](https://stackoverflow.com/questions/2209755/python-operation-vs-is-not)

[비슷한 질문을 해주시는 분들이 많아서 많이 해주시는 질문들을 모아보려고 합니다.]

얼마 전에 인프런을 통해 아래와 같은 질문을 들었다.

글의 요점은 비전공자이고, 나이도 많고, 공무원을 준비하셨고 등등 이제부터 시작을 해도 가능할까?라는 질문이었다.
사실 해당 질문을 받고, 이 질문자님의 이야기에 너무 공감이 나서 스스로 울컥했다.

나도 분명 저렇게 고민할 때가 있었고, 29살에 개발을 시작하려고 하니 오히려 주변에서 더 말이 많았다.

"너 이제부터 시작해서 개발자 할 수 있겠냐"
"너 20살때부터 전공으로 해 온 친구들과 경쟁해서 이길 수 있겠냐.."
등등 불안한 나의 마음을 더욱 불안하게만 만들었다.

사실 다 할 수 있는데, 사람은 마음 먹은건 다 할 수 있다고 생각한다.
그 마음 먹은게 힘든 것이지. 나에게도 그 마음 먹는게 정말 쉽지 않았다.

그래서 다시 자세를 고쳐잡고 질문에 답변을 해드렸다.

내가 해드린 답변의 전말은 아래와 같다.

""ㅇㅇ님 먼저 이렇게 문의 주셔서 감사합니다 :).

이건 전혀 다른 분야의 질문인 것 같아요. 저도 이랬던 시절이 있어서 ㅠㅠ 너무 공감가요...

만약에 저가 이 질문에 대해서 

"ㅇㅇ님 개발자 못하실거에요. 안되요. 6년동안 경력도 없으셨고 이미 나이도 32살이시고,
비전공에 이제와서 개발자한다고 하면 회사에서 뽑아 줄까요? 절대 안 뽑아주죠. 그러니 하지마세요"
이렇게 답변 드리면 개발자 안 하실건가요? 그냥 포기하실 건가요?

그렇게 포기하실거면 아예 도전을 안하시는게 맞아요.
근데 제 생각은 모든 일은 본인 생각에 달린 것 같아요. 그냥 "ㅇㅇ"님이 하고 싶으시면 하시면 되요.
할 수 있어요. 하지만 그 동안 못했던 것은 단지 그것을 하고자 제대로 마음을 먹지 않았던 거죠.
그렇다 보니 그 동안 다른 사람들의 말에 의견을 더 기울이셨을거에요. 내가 정말 원하는게 딱히 없었으니깐요.
하지만 이제 아시잔아요. 누구도 내 인생을 대신 살아주지 않고, 결국 선택에 대한 책임도 온전히 저가 져야한다는 것을요.

주변에 사람들이 많은 의견들을 줘요. 본인들의 생각을 바탕으로요.
그것이 나쁘다는 것이 아니에요. 단지 그 분들은 의견을 줄 뿐, 책임을 져주진 않아요.

내 인생이고, 결국 결정은 내가 하는 것이고, 거기에 대한 책임은 저가 온전히 지게 되어 있습니다.
"ㅇㅇ"님이 하고 싶으시면 하시면 되요. 다 하실 수 있어요. 
"난 00 때문에 안되, 난 000 때문에 안되" 이런 것보다는 "그럼에도 불구하고 해냈다"를 이야기하는
인생이 더 멋질 것 같아요! (저도 반성하고 갑니다 ㅠㅠ)

며칠 전에 이런 글을 보았어요.

"지금 나의 모습은 1년 전 내가 생각하던 나의 모습이다"

그리고 지금 저의 모습을 보면, 정말 딱 1년 전에 내가 생각했던

저의 모습이었어요. 그리고 불연듯 무서워지기도 했어요. 지금 나는 어떤 모습을

상상하고 있을까하구요.

"ㅇㅇ"님이 지금 미래의 모습을 개발자를 그리신다면, 1년 뒤에는 분명히

개발자를 하고 계실거에요.

화이팅입니다:) 

[[400만뷰 세바시 레전드 강연] 원하는 걸 이루고 싶다면_오현호 [세상을 바꾸는 시간 15분] / 변화 성장 청춘 열정 - YouTube](https://www.youtube.com/watch?v=oCtda6yxZ5c&t=12s)

이 영상도 추천드리고 싶어요!""

와 같은 말을 마치며 답변을 드렸다.
다시 보면 좀 주제 넘는 이야기를 했던 것 같기도 하고, 오히려 답변을 하고 나서 너무 과하게 이야기했나, 생각이 들기도 했다.
하지만 어떤 것이든 그것을 받아들이는 사람의 마음에 달렸다.

질문주신 질문자님은 그것을 좋은 방향으로 받아 들이신 것 같다.

 

원래 ㅇㅇ님이 좋은 분이셔서 좋은 방향으로 생각해주셔서, 너무 감사하고
또 다른 분에게 좀 더 나은 영향을 미쳤다는게 나에게 너무 큰 보람이다.

개발자를 고민하고 있는 모든 분들에게 도움이 되었으면 좋겠다.

[해당 글은 인프런 수업 개발자 취업 입문 개론
수업에서 질문 주신 부분을 바탕으로 글을 올렸습니다]

www.inflearn.com/course/개발자-취업-입문-개론?inst=b3611dbc

 

비전공자를 위한 개발자 취업 개론 - 인프런

개발자 취업 입문 개론 수업입니다. 평생 한 직업만 하실 게 아니라면, 꼭 한번은 개발자를 도전해 보시길 추천드려요! 비전공자 혹은 현재 다른 업무를 하더라도, 상관없습니다. "쌀 팔다 개발

www.inflearn.com

 

 

[드디어...!!!!!!!!!!!!! 추가 소식 전달!]

안녕하세요 쌀 팔다 개발자하고 있는
김병욱입니다! 

저 역시도 29살의 나이에,
처음으로 개발이라는 것을 배우면서
무엇을 해야 할지 몰라 많은 시행착오를 겪었습니다.

그리고 현재는 잘 성장하여,
3년차 백엔드 개발자로 일하고 있습니다.
새롭게 개발 시작하시는 분들이 저와 같은 시행착오를

겪지 않았으면 좋겠다는 생각으로 개발자가 된 이후로
지속적으로 강의를 해왔었는데, 항상 시간이 부족해서
아쉬움이 가득이었습니다.

그래서, 정말 큰 맘 먹고 올해 1월부터 책을 적기 시작했습니다.
회사 출근 전 퇴근 후 최대한 시간을 내서(피,땀, 눈물 ㅠㅠ), 제가 알고 있는,
그리고 부족한 것은 주변 개발자분들에게 물어가며
정말 열심히 적었습니다!

그리고 10개월이 지난, 이제서야 이 책이 정말 곧 빛을 보려고 합니다.
개발 시작하시는 분들이 정말 꼭 읽고 시작했으면 좋겠다는 생각에
용기내어 이렇게 글을 적습니다. 최소 2개월은 save하실 수 있으실거에요!
(책 팔아서 돈을 번다는 것은 정말... 너무 어려운 일이고, 그러고 싶지도 않습니다.)

정말 이 책이 정말 개발 시작하려는 분들에게는
조금이라도 도움이 되었으면 좋겠습니다.
그것이면, 전 정말 만족할 것 같습니다.

현재 텀블벅에서 펀딩 진행 중입니다.
주변에 개발자 하시고 싶어하시는 분들이 있다면 많이 추천해주세요!
https://tumblbug.com/tomorrow_programmer?ref=discover

 

취업까지 로켓배송! 개발자 취업 가이드 [오늘부터 개발자]

개발자 취업까지 수백시간 줄여줄 비전공자 취업 입문 개론 [오늘부터 개발자]

www.tumblbug.com

감사합니다!

 

 

docker란?

  • 컨테이너 기반의 오픈소스 가상화 플랫폼

  • 도커는 환경 분리를 도와준다.

  • 작업물을 서버에 올렸는데 작동하지 않는다.

    • 나의 작업 환경은 윈도우고, 서버는 리눅스

  • docker를 통해 다른 머신에서도 같은 환경을 조성해준다.

 

docker image

 

  • 컨테이너 실행에 필요한 파일과 설정값등을 포함하고 있는 것으로 상태값을 가지지 않고, 변하지 않는다.

  • 컨테이너는 이미지를 실행한 상태라고 볼 수 있고 한 이미지로 여러개의 컨테이너를 생성할 수 있다.

  • 이미지는 컨테이너를 실행하기 위한 모든 정보를 가지고 있기 때문에 더 이상 의존성 파일을 컴파일 하고 이것저것 설치할 필요가 없다.

  • docker 이미지는 docker hub에 등록하거나, docker registry 저장소를 직접 만들어 관리할 수 있다.

 

dockerfile

  • 이미지를 만들기 위해 dockerfile이라는 파일에 DSL(Domain-specific-language 언어를 이용하여 이미지 생성과정을 적는다.) 

  • docker에서 사용하고 싶은 환경을 설정한다.

  • 우분투, 파이썬, 깃 등등

  • 이 파일을 통해 로컬에서와 서버에서 같은 환경을 조성해준다.

 

docker 특징

  • docker는 서로 독립적이라서 하나의 서버가 많은 수의 컨테이너를 가질 수 있다.

  • docker 덕분에 매번 새로운 서비스를 만들 때마다 새로운 서버를 사고 설정할 필요가 없다.

  • 그냥 docker container를 더 늘리면 된다.

  • 원하는 개발 환경을 파일에 저장하면 docker는 내가 원하는 해당 환경을 시뮬레이션 해준다.

  • 각각의 환경들은 독립적으로 존재하기 때문에 무슨 환경이든 모듈식으로 관리 가능하다.

    • python server, java server, db server 등 모두 따로 구매해줄 필요 없다.

 

볼륨(데이터)을 공유 하지 않으면 서로 같이 쓸 수가 없다.

  • docker volume

  • docker를 사용하다보면 container안에서의 데이터 휘발성 때문에 

  • volume을 사용하게 된다.

  • 하나의 서버안에서도 여러개의 docker가 돌 수 있고, 

  • 특정 docker안에서 만들어진 파일은, 공유할 수 없다.

  • 이런 불편함을 막기 위해 서버 안에 디스크를 mount 해놓고 사용하면 된다.

  • docker 컨테이너는 언제든지 추가되고 삭제 될 수 있기 떄문에 데이터는 반드시 컨테이너 내부가 아닌 외부 스토리지에 저장해야 한다.

 

참고 : [초보를 위한 도커 안내서 - 이미지 만들고 배포하기](https://subicura.com/2017/02/10/docker-guide-for-beginners-create-image-and-deploy.html)

'서버' 카테고리의 다른 글

nslookup 이란  (0) 2020.08.31
초보자를 위한 REST API  (0) 2020.06.21
NAS란(나스란)  (0) 2020.06.01
MOUNT란  (0) 2020.06.01
디자인패턴  (0) 2020.03.16

NAS란 Network Attached Storage의 줄인 말로

네트워크와 연결된 저장소이다.

 

네트워크로 연결되어 있기 때문에 언제 어디서든

pc, 모바일로 나스에 저장된 파일에 접근할 수 있다.

 

쉽게 이야기하면 저장소인데, 

이게 네트워크로 연결되어 있다고 생각하면 편할 것 같다.

 

따라서 다른 곳들에서도 NAS의 장점으로 드는게

확장 가능하며, 비용이 저렴하다는 것이다.

 

요즘은 클라우드 서비스들에서도 NAS를 

제공하고 있다.

 

참고 : [NAS - Storage - NAVER CLOUD PLATFORM 네이버 클라우드 플랫폼](https://www.ncloud.com/product/storage/nas)

[클라우드 파일 스토리지란 무엇입니까? – AWS](https://aws.amazon.com/ko/what-is-cloud-file-storage/)

 

이렇게 NAS 볼륨을 여러 대의 서버에서 함께 mount해서 사용하면

어떤 서버에서는 해당 데이터에 접근하여 사용할 수 있다.

 

근데 mount해서 사용한다는 것은 

반대로 종속성을 가지게 된다는 말이다. 

 

우리가 NAS에 데이터를 넣는 방법에는 여러가지가 있겠지만,

실제 가능한 상황을 2가지로 생각할 수 있다.

 

  1. docker내에서 해당 volume을 mount해서 사용하는 것

  2. 직접 NAS에 파일을 업로드 하는 것

 

현재 우리는 1번의 혀태로 사용하고 있는데,

docker내에서 해당 volume에 바로 접근할 수 있어서

굉장히 편하다.

 

하지만 이런 형태는 종속성을 갖는다.

즉 서버를 새롭게 만들 때 마다 해당 환경을 다시 셋팅해줘야하는

불편함이 이다.

 

그에 비해 2번으로 구현을 해놓으면 종속성이 없어진다.

 

2번 방법이 관리 측면에서는 더 좋은 방법이라고 볼 수 있지만,

계속 1번으로 하고 있는 것은, 그것보다 편한 것이 더 큰게 아닐까?

 

갑자기 2번으로 하려고 하니 머리가 아프다 질끈 @.@

'서버' 카테고리의 다른 글

초보자를 위한 REST API  (0) 2020.06.21
Docker란(도커란)  (0) 2020.06.02
MOUNT란  (0) 2020.06.01
디자인패턴  (0) 2020.03.16
nginx 기본에 대해서 알아보기  (0) 2020.01.20

# 마운트에 대한 간단한 정리


우리가 컴퓨터를 사용할 때 외장하드를 연결하면

D드라이브와 같이 새로운 폴더가 내컴퓨터에 만들어진다.

 

이렇게 디스크와 같이 물리적 장치를 특정 위치 즉 디렉토리에

연결시켜주는 것을 마운트라고 한다.

 

윈도우에서는 이것을 직접해줘서 

우리가 마운트라는 이야기를 들어본적이 없지만,

리눅스에서는 직접 MOUNT 작업을 해줘야 한다.

 

리눅스는 윈도우와 다르게 하나의 디렉터리로부터 뻗어지는

single directory tree구조를 갖고 있다.

따라서 마운트를 진행하면 /media/cdrom과 같은 형태를 띄게 된다.

 

리눅스 서버에서 mount라는 명령어를 입력하면

현재 mount 되어 있는 것들의 list를 쭉 볼 수 있다.

 

추가적으로 df 명령어를 통해 현재 마운트 된 disk들의 

정보를 볼 수 있다. 

 

현재 우리 서버에서는 nas를 mount해서 사용 중에 있는데,

해당 nas를 mount 해준다는 정보는 docker-compose안에 

volumes 설정에 있다. 

 

'서버' 카테고리의 다른 글

Docker란(도커란)  (0) 2020.06.02
NAS란(나스란)  (0) 2020.06.01
디자인패턴  (0) 2020.03.16
nginx 기본에 대해서 알아보기  (0) 2020.01.20
몽고DB와 NoSQL  (0) 2019.12.04

JAVASCRIPT datetime validation 하는 방법


# 가장 간단한 방법 (Date.parse 이용)

IsValidDate = Date.parse("2020-05-01")

if (isNAN(IsValidDate)){
    alert("not a date")
}

참고 : [Date.parse() - JavaScript | MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Date/parse)

참고 : [꿀벌개발일지 :: Date.parse() 가 지원하는 날짜 포맷](https://ohgyun.com/540)

 

# moment js 사용

<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.16.0/moment-with-locales.min.js"></script>

valid_from_date = moment(from_date, "YYYY-MM-DD HH:mm:ss", true).isValid();

 

참고 : [Moment.js | Docs](https://momentjs.com/docs/#/parsing/string/)

참고 : [momentjs - Is there a way to validate time using Moment JS? - Stack Overflow](https://stackoverflow.com/questions/26040397/is-there-a-way-to-validate-time-using-moment-js)

 

# datepicker.parseDate 이용

function validateDate(dateField) {
       try{
           $.datepicker.parseDate('mm/y', dateField, null);
       }
       catch(error){
           alert(error);
       }
}

  

참고 : [datepicker.parseDate - jQuery Forum](https://forum.jquery.com/topic/datepicker-parsedate)

참고 : [validation - How to validate date with format "mm/dd/yyyy" in JavaScript? - Stack Overflow](https://stackoverflow.com/questions/6177975/how-to-validate-date-with-format-mm-dd-yyyy-in-javascript)

 

# 시간 체크하는 함수 사용

function isDatetime(d) {
    var re = /[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]/;
    //         yyyy -       MM      -       dd           hh     :   mm  :   ss
  return re.test(d);
}


if (!isDatetime("2020-05-01 11:00:00")){
    alert('시간 형태가 잘못되었습니다.')
}

 

참고 : [[Javascript] 문자로 된 날짜(Datetime) 유효성 검사 (yyyy-mm-dd hh:ii:ss) :: 확장형 뇌 저장소](https://extbrain.tistory.com/33)

 

[이슈]
celery beat를 이용하여 cron을 대처하기 위해
celery beat를 붙였다.

잘 작동하였다.

 

특히 beat의 스케줄은 crontab으로 관리하였는데, 
이슈는 crontab에 등록해놓은 시간이...  제시간에 실행되지 않고, 9시간 뒤의 task를 땡겨와서 실행하고 있었다... 하...

import datetime


app = Celery('****')
app.conf.timezone = "Asia/Seoul"
app.conf.enable_utc = False

app.now = datetime.datetime.now

app.conf.beat_schedule = {
    'alliance_order_daily_upload_worker':{
        'task': 'celery_batch.tasks.alliance_order_daily_upload',
        'schedule': crontab(minute=0, hour=1),
        'options': {'queue': 'slow_tasks'},
        'args': ()
    },
}

 

위와 같이 설정하였고, 기대는 새벽 1시에 beat가 돌기를 기대하였다.

하지만 기대와 다르게 업무 시간이었던 오후 4시에 crontab이 task를 queue에 넣었다.
즉 9시간 뒤의 task를 queue에 넣은 것이다.

도대체 뭐가 꼬인 걸까?
분명 UTC와 한국 시간의 차이가 9시간 이고

python shell에서 
생성된 app의 
app.now()를 체크해보면 한국 시간으로 체크되고 분명 한국 시간과 같은데, 
또 다시 9시간 뒤에 것을 떙겨와서 queue에 쌓는다라...

 

너무 이상한 점이 많았다.
beat에 등록된 crontab 시간을 한국 시간이 아니라 UTC 시간으로 보았다면, UTC 1시는 한국 시간 10시니깐 
10시에 시작을 했어야 했는데, 새벽 1시에 등록된 task가 한국 시간 오후 4시에 시작이 되었다. 

그럼 crontab에 등록된 시간이 UTC인가??
아니다 한국 시간이 맞다 ㅠㅠㅠㅠ..

In [1]: from danoshop.celery import *


In [2]: app.now()
Out[2]: datetime.datetime(2020, 5, 14, 16, 50, 38, 574737, tzinfo=<DstTzInfo 'Asia/Seoul' KST+9:00:00 STD>)

이렇게 나온다.

[Spoqa 기술 블로그 | 파이썬의 시간대에 대해 알아보기(datetime.timezone)](https://spoqa.github.io/2019/02/15/python-timezone.html)

위의 문서를 참고해보면

import datetime
from pytz import timezone, utc

KST = timezone('Asia/Seoul')

now = datetime.datetime.utcnow()
# UTC 기준 naive datetime : datetime.datetime(2019, 2, 15, 4, 18, 28, 805879)

utc.localize(now)
# UTC 기준 aware datetime : datetime.datetime(2019, 2, 15, 4, 18, 28, 805879, tzinfo=<UTC>)

KST.localize(now)
# UTC 시각, 시간대만 KST : datetime.datetime(2019, 2, 15, 4, 18, 28, 805879, tzinfo=<DstTzInfo 'Asia/Seoul' KST+9:00:00 STD>)

utc.localize(now).astimezone(KST)
# KST 기준 aware datetime : datetime.datetime(2019, 2, 15, 13, 18, 28, 805879, tzinfo=<DstTzInfo 'Asia/Seoul' KST+9:00:00 STD>)

 

와 같이 나와있는데 결국 위의 shell에서 나온 것에 대한 해석은 해당 시간을 3번째 케이스로 보기에는 시간대가 한국과 같아 제일 아래로 보는게 맞다고 판단하였다.

 

그럼 app.now의 시간은 지금 시간은 맞아.... 지금 시각이야.
crontab에 등록된 것도 지금 시각이야...

근데 9시간 뒤의 것을 먼저 실행한다...??

어떤 원인인지 정확히 파악은 못했다.
app.now의 시각은 분명히 한국 시간인데, 이것을 beat에서는 어느중간 UTC 시간으로 바꾸고, 9시간 뒤의 crontab을 가지고 온다고 하니 이야기가 맞아 떨어졌다.

 

해결

해결책 1. 

  • 현재 app의 시간을 utc시간으로 바꾸어 준다.

  • 따로 crontab의 시간을 바꾸지 않아도 된다.

# app.conf.timezone = "Asia/Seoul" : CELERY_TIMEZONE에 있어서 주석처리

# app.conf.enable_utc = False 어디에 쓰는 걸까...? CELERY_ENABLE_UTC = False 에 있다.

app.now = datetime.datetime.utcnow

  • 단 djcelery_periodictask; db의 last_run_at time이 UTC 기준으로 들어가게 된다. 즉 한국 시간보다 9시간 뒤의 시간이 저장된다.

 

해결책 2

  • app.now의 값은 그대로 놔두고 crontab의 시간을 9시간씩 더해서 해준다.

  • 그러면 너무 쉽지 않으니깐.. 만들어진 함수를 쓰자

def convert_cron_hour(hour):

    now = datetime.datetime.now()

    utc_datetime = datetime.datetime(

        now.year, now.month, now.day, hour, 0, 0, 0, tzinfo=pytz.timezone("UTC")

    )

    local_datetime = utc_datetime.astimezone(pytz.timezone('Asia/Seoul'))

    return local_datetime.hour





app.conf.beat_schedule = {

    'alliance_order_daily_upload_worker':{

        'task': 'celery_batch.tasks.alliance_order_daily_upload',

        'schedule': crontab(minute=0, hour=convert_cront_hour(1)),

        'options': {'queue': 'slow_tasks'},

        'args': ()

    },



  • 위와 같이 해주면 알아서 9시간 뒤로 cronschdule를 저장해준다. 

 

하... 원인을 좀 더 자세히 파악해보고 싶은데,
영혼까지 털털 털려서 ㅠㅠ 일단은 추후에 좀 더 알아봐야겠다

 

 

 

 

 

 

 

 

 

 

서버리스

  • 서버가 없다?

  • 백엔드에 서버가 없다??

  • 백엔드인데 직접 서버를 관리하지 않음

 

옛날의 어플리케이션 배포 방법

  • 직접 서버를 구매하여 전원을 꼽고

  • 즉 서버의 하드웨어와 소프트웨어 2개를 모두 관리해야 했다.

 

새롭게 등장한 EC2

  • 이제는 거실에 서버를 설치하는게 아니라, EC2에서 서버(하드웨어)를 빌려서 관리

  • 구글, 아마존, MS에서 서버의 하드웨어 부분을 책임지고 관리해준다.

  • 서버의 소프트웨어 부분은 직접 관리해줘야 한다.

 

하지만 소프트웨어 관리도 힘들다

  • 업데이트

  • 보안

  • 데이터백업 등등 해야 할 것이 많음

 

서버리스를 활용하면

  • 백엔드를 서버에 올리는게 아니다.

  • 백엔드를 작은 함수단으로 쪼개서, 직접 관리하지 않는 서버로 올린다. AWS 람다

  • 서버리스가 아닌 경우 서버는 24시간 돌아가고 있다. 언제나 요청에 응답할 준비를 하고 있음

  • 서버리스는 업로드해 놓고 잠을 자고 있다가, 요청이 오면 잠을 깨워서 실행

 

서버리스의 장점

  • 매번 실행하여 대기하고 있지 않아도 된다.

  • 호출한 만큼만 비용을 내개 되면서 급격하게 저렴해짐

  • 유저가 많아지면 함수를 더 많이 만들어서 실행하여 버림으로서 포퍼먼스에도 영향이 없음

  • 제품을 빠르게 출시가능하다.

  • EC2처럼 코드를 올린다고 해서 돈을 내는 것이 아니다.(적은 비용으로 유지 가능)

 

서버리스의 단점

  • cold start 함수가 잠들어 있어서 깨우는데 시간이 걸린다.

    • aws 람다에서는 많이 쓰는 함수는 대기 하고 있음

  • 서버 제공자에 너무 의지하게 된다.

    • 서버리스에서 다른 쪽으로 서버를 옴기는 것이 쉽지 않다.

    • 어플리케이션의 구조자체가 바뀌게 된다.

 

누가 써야 하나?

  • 사이드 프로젝트를 진행 중이면 추천

  • 코드에만 집중하면 된다. 서버 관리 및 설정에 시간을 쓰지 않아도 된다.

 

출처 : www.youtube.com/watch?v=ufLmReluPww

- 해당 정리는 pycon 2015 celery 강의 자료와 pycon 2019 celery 강의자료를 바탕으로 만들었습니다.
아래의 2개의 영상은 celery를 이해하는데 매우 좋습니다. 꼭 보시기를 추천드립니다!

- 참고:
정민영: Celery의 빛과 그림자 - PyCon Korea 2015 - YouTube](https://www.youtube.com/watch?v=3C8gBRhtkHk)

셀러리 핵심과 커스터마이제이션 - 이지훈 - PyCon.KR 2019 - YouTube](https://www.youtube.com/watch?v=vGPyjJ1jWUs)

 

Celery

  • Distributed task queue 혹은 종합적인 비동기 처리기

비동기 처리기?

  • 비동기 처리기는 동기적으로 수행하지 않아도 되는 일들을 처리해주는 역할

  • 결과를 즉시 받을 필요가 없거나 지연하여 처리해야 되는 일들을 보통 처리

  • 물론 그것이 제대로 처리가 되지 않아도 된다는 이야기는 아니기 떄문에 별도라 잘 만들어진 처리기가 필요하다. => 항상 잘 처리되고 유실도 되지 않아야 한다.

 

why celery?

  • 완전 쉽게 연동할 수 있다.

  • 우리가 상상하는 모든 기능을 제공한다.

  • 일단 남들이 제일 많이 쓴다.

 

Celery 시작하기

  1. 먼저 Celery 인스턴스를 생성해준다.
    이 인스턴스를 통해 작업 생성 및 작업 관리와 같이 모든 작업의 시작점으로 사용된다.

  2.  
from celery import Celery

app = Celery('tasks', broker='pyamqp://guest@localhost//')

@app.task
def add(x, y):

    return x + y
  • tasks라는 Celery 인스턴스를 생성하고 broker의 url을 지정해준다.

 

함수 비동기 실행하기 

from tasks import add

add.delay(1,2)

 

Worker 실행하기

celery -A tasks worker --loglevel=info

 

celery 여러가지 기능들

  • 실행이 언제 될지 지정할 수도 있다.

 

# Crontab을 대체할 수 있다.

  • CELERYBEAT_SCHEDULE로 대체

  • + 버전 관리가 가능하다.

 

# Celery 상태관리

  • celery event => 쓰기 위해서는 worker를 킬 때 -E 명령어를 같이 써줘야 한다.

  • celery flower

 

BUT 성장을 위한 가장 큰 문제는 Celery

 

# 잘 알고 써야 하는 Celery

  • 적은 규모에서 간편하게 쓰기에는 더 없이 훌륭하다

  • 의외로 조금만 커져도 신경써야 할 부분이 많다.

  • 처음부터 고려하지 않으면 알 수 없는 이상 동작처럼 느껴질 수도 있으니 주의가 필요한 부분이 있다.

 

# Broker

  • 어떤 녀석이 일(tasks)를 처리할지 중개 (broker)

  • RabbitMQ - AMQP처럼 동작?

  • AMQP란??

  • RabbitMQ이 가장 default broker이다.

# AMQP

- 최소한 한번은 전달된다.
- But 이 말은 여러번 전달 될 수도 있다.

 

# 다른 Broker를 쓰면 문제 점(Rabbitmq 가 아닌)

  • ACK

  • visibility_timeout

 

# ACK

  • worker가 진행한 일에 대한 결과 혹은 상태를 broker에게 전달

  • 그럼 broker는 해당 업무를 그냥 done처리해버림

- rabbitmq가 아닌 다른 broker들에서는 visibility_timeout으로 ACK를 구현

# visibility_timeout

# ack + visibility_timeout

  • ack가 오지 않으면 다시 message를 전송한다.

  • 이것을 visibility_timeout을 통해서 구현을 해놓음

  • 따라서 visibility_timeout내에서 ack가 전달되지 않으면 task가 중복실행된다.

  • eta, countdown 시간보다 visibility_timeout이 커야한다.

 

 

# Redis를 broker로 쓸 때

  • Redis는 메모리가 부족한 상황에서 임의로 key를 삭제될 수 있다.

  • 즉 task를 받아도 삭제 될 수 있다.

 

# Celery 구조

  • web application에서 일을 Broker로 넘긴다.

  • 나머지는 Broker가 알아서 Worker로 분배

# prefetch의 배신

  • 샐러리의 가장 큰 그림자

  • prefetch는 broker에서 tasks(message)들을 그냥 미리 땡겨두는 거다?

  • Prefetch is a term inherited from AMQP that is often misunderstood by users, The prefetch limit is a limit for the number of tasks (messages) a worker can reserve for itself

  • 실제 동작은 처음 4개의 공간에 당겨왔던 tasks를 다 끝내야지  이후에 tasks를 가지고 와서 새롭게 시작한다.

  • prefefetch된 단위 전체의 작업을 소비해야(ack) 다음 prefetch가 수행된다.

  • task가 비워지는대로 다음 task를 broker에서 가져올꺼라고는 일반적인 기대와는 다르다.

 

- 긴 task에 대해서는  worker_prefetch_multiplier을 1로 설정해주고, acks_late를 True로 해주면 실행 중인 task 1개를 끝낼 때 마다 떙겨오기 떄문에 불필요 하게 다른 worker들이 쉬게 되는 일이 없다.

# Prefork

  • prefetch limit은 broker에서 worker로 분배하는 로직이고

  • 해당 worker안에서 분배하는게 prefork 

  • -O fair같은 경우 해당 worker안에서 Master가 분배하는 방법

 

# Prefork pool prefetch

 

  • 현재 일하고 있지 않은 worker에게 일을 주는게 아니라 지속적으로 그냥 순차적으로 쌓아버림

  • -ofair => 일 안하고 있는 worker에서 일을 시킴

 

해결책

  • task를 한 큐에 담지 마세요 

  • prefetch의 특성상 평균 수행 시간이 비슷한 것들이 같은 queue에 있는 것이 성능상 훨씬 유리하다

  • task의 절대적인 수 자체도 중요한 요소이다.

  • 처리의 중요도/ 시급도 에 따른 분류도 중요하다

  • 위와 같은 요소를 고려해서 Queue를 나눠주세요

  • CELERY ROUTE

  • worker를 특정 queue를 붙여 둔다.

 

정말 간단한데 성능에 큰 영향을 주는 또 다른 요소

  • ingore_result => 이게 default로 켜져있다.

  • Celery는 기본적으로 수행 결과(return)을 저장 해야 작업이 끝난다.

  • 하지만 대부분 TASK내에서 직접 결과를 다른 곳에 저장하지, return 자체를 쓰는 경우는 드물다.

  • 보통은 TASK 연계를 하는 경우만 필요하다.

  • 결과를 저장하는 비용이 적지 않기 때문에 이걸 끄기만 해도 무척 성능이 좋아진다.

 

+ Recent posts