반응형

백엔드 Back-end 95

Q. 파이썬 팩키지 pika로 래빗엠큐RabbitMQ로 접속할 때 나타나는 에러는?

A. 연결 설정 오류로 StreamLostError: ('Transport indicated EOF',) 등 에러가 발생한다. 래빗엠큐 AMQP 기본 port는 5672다. 그런데 엉뚱한 port를 입력한다면? import pika connection = pika.BlockingConnection( pika.ConnectionParameters(host='localhost', port=3306)) channel = connection.channel() channel.queue_declare(queue='hello') channel.basic_publish(exchange='', routing_key='hello', body='Hello World!') print(" [x] Sent 'Hello World..

Q. 래빗엠큐RabbitMQ에서 ack의 의미는?

A. acknowledgment 확인, 인지란 뜻이다. 아래 출처를 보면 ack(nowledgement)란 표현을 볼 수 있다. 발음은 애크라고 읽는 것 같다. In order to make sure a message is never lost, RabbitMQ supports message acknowledgments. An ack(nowledgement) is sent back by the consumer to tell RabbitMQ that a particular message had been received, processed and that RabbitMQ is free to delete it. 출처: Message acknowledgment 파이썬 pika 팩키지를 활용할 경우 auto_ack..

Q. 파이썬으로 래빗엠큐RabbitMQ 메시지를 보내고 받고 싶다면?

A. pika 팩키지를 활용하면 된다. RabbitMQ 공식 홈페이지에 pika 튜토리얼이 있다. 튜토리얼 그대로 따라하면 쉽게 테스트를 해볼 수 있다. 도커로 RabbitMQ 실행하기 /Users/taptorestart 부분은 여러분의 경로로 수정하면 된다. 참고: rabbitmq dockerhub docker run --name rabbitmq \ -p 5672:5672 \ -p 15672:15672 \ -v /Users/taptorestart/rabbitmq:/var/lib/rabbitmq \ -d rabbitmq:3.11.10-management 이 때 중요한 것은 -p 5672:5672도 적어줘야 한다는 점이다. 실행했으면 접속하자. http://localhost:15672/ 로 접속하고 id,..

Q. 래빗엠큐RabbitMQ를 종료하고 재시작하면 큐에 쌓여 있던 메시지가 사라질까?

A. Durability 설정이 durable이고, Delivery mode가 persistent 라면 메시지가 남아 있다. RabbitMQ 실행 우선 RabbitMQ를 실행하자. 도커를 사용한다면 아래 명령어로 쉽게 RabbitMQ를 실행할 수 있다. /Users/taptorestart 부분은 여러분의 경로로 수정하면 된다. 참고: rabbitmq dockerhub docker run --name rabbitmq \ -p 5672:5672 \ -p 15672:15672 \ -v /Users/taptorestart/rabbitmq:/var/lib/rabbitmq \ -d rabbitmq:3.11.10-management 실행했으면 접속하자. http://localhost:15672/ 로 접속하고 id, ..

Q. 깃허브 풀리퀘스PullRequest 때 테스트를 자동화하는 방법은?

A. 깃허브GitHub 워크플로우workflows를 활용하면 쉽게 테스트를 자동화할 수 있다. pytest를 실행하는 예 깃허브 저장소에 .github 폴더를 만들고 그 아래 workflows를 만든다. 그리고 아래 내용을 담은 yml 파일을 만든다. 이름은 아무 거나 가능하다. tests.yml로 만들자. name: Test on: pull_request: branches: - main jobs: build: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: '3.6' - run: | python -m pip install --upgrade pip pyth..

테스트 주도 개발이 기능부터 개발하는 것보다 더 빠르고 안정적이다

테스트 주도 개발Test Driven Development(TDD)란? 켄트 벡이 창시한 개발 방법론으로 테스트부터 작성하는 개발 방법론이다. 먼저 통과하지 못할 테스트를 작성하고 테스트를 통과하도록 코드를 작성한다. 그 뒤 코드를 개선, 리팩터링하고 테스트하는 과정을 반복하는 식이다. 리팩터링 책으로 유명한 마틴 파울러가 추천하는 개발방식이다. TDD의 가장 큰 특징은? 테스트 주도란 명칭처럼 테스트부터 작성한다! 기능부터 작성 → 테스트가 아니라, 테스트 → 기능 코드 작성이다. 파이썬을 예를 들면 테스트부터 작성한다. def test_sum() assert sum(1, 2) == 3 당연히 sum 함수 구현이 안 되어 있으니 테스트는 실패한다. 위 테스트가 성공할 때까지 sum함수를 구현하는 식이다..

테스트Test 기반 없는 코드 리뷰Code Review의 비효율성과 위험성

코드 리뷰 코드 리뷰를 하는 리뷰어는 보통 눈으로 변경된 코드를 읽고 자신의 의견을 말한다. 코드 전체가 아니라 바뀐 부분만 주로 본다. 따라서 코드 변경에 따른 의도하지 않은 부작용까지는 생각하기 어렵다. 리뷰어는 의견을 낼 뿐 책임지지 않는다. 책임은 코드 작성자 몫이다. 테스트 기반 없는 코드 리뷰의 비효율성 리뷰어가 제시한 사소한 변경이라도 만에 하나 있을 부작용 확인을 위해 작성자는 매번 테스트를 해야 한다. 테스트가 없는 상태라면 일일이 리뷰어의 의견을 반영할 때마다 관련 코드 전반을 테스트 해야 한다. 리뷰어들이 같은 시간 한꺼번에 리뷰해주면 좋겠지만 A 리뷰어, B리뷰어가 리뷰하는 시간은 다른 경우가 대부분이다. 따라서 리뷰를 받을 때마다 테스트하게 된다. 테스트 기반 없는 코드 리뷰의 위..

Q. 장고Django에서 같은 이름으로 인덱스index를 추가할 수 있을까?

A. 장고 2.2.x 버전에서는 추가할 수 있고, 장고 3.2.x 버전에서는 추가할 수 없다. 장고 버전 3.2.x에서 인덱스 name을 같은 이름으로 하면 유일한 이름이 아니라고 makemigrations에서 에러가 발생한다. 사례 예제 코드 DRF - CRUD를 활용해서 테스트 해보자. 테스트를 위해서 모델에 아래처럼 인덱스를 추가하자. class Category(models.Model): name = models.CharField(max_length=200) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = "category" ..

Q. 장고Django 쿼리셋Queryset에서 select_related보다 prefetch_related 사용을 권장하는 이유는?

A. 조인 분해를 통해서 성능상에서 이익을 얻는 경우가 많기 때문이다. select_related와 prefetch_related SQL 쿼리 예 Q. 장고Django ORM에서 n+1 이슈란? n+1 이슈 해결 방법은?란 글에서 다뤘듯이 select_related는 INNER JOIN을 사용한다. prefetch_related는 INNER JOIN을 사용하지 않고 조인을 분해한다. 앞의 글에서 다뤘던 예를 다시 가져오면 아래와 같다. select_related SQL 쿼리 예 SELECT `app_beverage`.id, `app_beverage`.name, `app_beverage`.category_id, `app_beverage`.price, `app_beverage`.is_available, `a..

Q. MySQL에서 count(*)과 count()의 차이는?

A. count(*)은 WHERE 조건이 있다면 조건에 해당하는 모든 행수를 얻을 수 있고, count(칼럼명)의 경우는 해당 칼럼명의 데이터를 조회해서 NULL이 아닌 데이터가 들어 있는 행수를 얻을 수 있다. InnoDB 엔진 예 간단히 coffee란 테이블을 만들어서 5개의 데이터를 넣었다. 맨 마지막 5번 데이터는 NULL을 넣었다. 이 상태에서 SELECT count(*) FROM coffee; 를 실행하면 5가 나온다. 만약 칼럼명인 name으로 SELECT count(name) FROM coffee; 하게 되면 4가 결과로 나온다. 두 경우 모두 Query Cost를 살펴보면 0.75이고 Full Table Scan이 발생한다. SELECT count(*) FROM coffee; SELECT..

Q. 장고Django ORM에서 n+1 이슈란? n+1 이슈 해결 방법은?

A. n+1 이슈란 목록을 조회할 때 주로 발생하며, 연관된 데이터까지 가져올 때 데이터 개수(n으로 표현)만큼 추가로 데이터베이스를 조회하는 이슈를 뜻한다. 해결 방법은 쿼리셋을 가져올 때 select_related 또는 prefetch_related를 사용하는 것이다. n+1 이슈 예아주 간단히 카페 메뉴 정보를 저장하는 API 서버를 만드는 경우를 생각해보자. (예제 코드: DRF - CRUD) 카테고리 테이블과 음료 테이블 두 개가 있다. 카테고리 테이블에는 커피, 티를 입력했고, 음료에는 카테고리를 커피로 선택해서 아메리카노, 카페 라떼, 에스프레소, 카푸치노를 입력했다. 카테고리 테이블과 음료 테이블 사이의 관계는 카테고리 1개에 여러 음료가 연결될 수 있으니 1 : n(일 대 다) 관계다. ..

Q. 쿠키cookie에서 HttpOnly 속성의 기능은?

A. 자바스크립트로 document.cookie로 쿠키에 접근할 수 없게 된다. Set-Cookie: =; HttpOnly 위 예처럼 Cookie를 설정했다면 자바스크립트로는 접근이 안 된다. 실제 사례 아래 예(소스코드)를 보면 sessionid의 경우 HttpOnly인 상태인 것을 알 수 있다. document.cookie로 sessionid를 얻으려고 확인해보면 값이 없는 것을 확인할 수 있다. MDN 설명을 보자. A cookie with the HttpOnly attribute is inaccessible to the JavaScript Document.cookie API; it's only sent to the server. For example, cookies that persist in s..

Q. 장고Django의 View와 DRF의 APIView의 인증 관련 차이는?

A. CSRF 보호 제외 여부다. DRF(Django Rest Framework)의 APIView 자체도 from django.views.generic import View를 상속받아서 만든 것이다. View에 API에 주로 쓰도록 기능이 추가된 형태로 볼 수 있다. APIView의 as_view 메소드를 보자. @classmethod def as_view(cls, **initkwargs): """ Store the original class on the view function. This allows us to discover information about the view when we do URL reverse lookups. Used for breadcrumb generation. """ if i..

Q. HTTP/0.9에서 사용한 HTTP 요청 메소드는?

A. GET이다. GET밖에 없었다. This request consists of the word "GET", a space, the document address , omitting the "http:, host and port parts when they are the coordinates just used to make the connection. (If a gateway is being used, then a full document address may be given specifying a different naming scheme). 출처: The Original HTTP as defined in 1991 요청과 응답 예가 아래와 같았다고 한다. 출처: HTTP/0.9 – 원-라인 프로토콜 ..

Q. HTTP GET 메소드로 Body에 데이터를 실어서 보내면 데이터가 갈까?

A. 간다. 패킷을 보면 전송되는 것을 확인할 수 있다! GET의 경우 Body에 데이터를 실어서 보내도 안 갈거라고 생각했는데 간다는 얘기를 듣고 직접 해보게 되었다. 같은 네트워크 상에 두 컴퓨터를 켜서 요청을 받는 컴퓨터에 WireShark를 설치했다. 192.168.1.23 컴퓨터에서 PostMan으로 메소드는 GET 요청 바디(Request Body)에 Content-Type: application/x-www-form-urlencded로 userId=1이란 값을 입력하고 전송 버튼을 눌렀다. 따라서 192.168.1.23 컴퓨터가 Source 출발지, 192.168.1.2가 Destination 도착지이다. 192.168.1.2에서 WireShark를 실행해서 패킷을 확인했다. userId=1을..

Python requests와 pytest로 REST API 서버 테스트하기

자바스크립트 기반인 frisby.js로 REST API 서버 테스트를 할 수도 있지만, 파이썬이 익숙하다면 requests로도 쉽게 구현할 수 있다. 준비: API 서버 실행 우선 FastAPI 예제를 실행한다. 예제코드는 FastAPI - Email Login with Password.. 여기 있다. localhost:8000/docs로 접속했을 때 아래처럼 뜬다면 정상 작동하고 있는 것이다. API 테스트 코드 작성 위에 브라우저로 확인한 것을 그냥 코드로 아래처럼 작성해서 확인할 수도 있다. import requests def test_get_docs(): endpoint = "http://localhost:8000/docs/" response = requests.get(endpoint) asser..

Q. 장고Django에서 외래키ForeignKey 필드의 id를 얻을 때, field.id를 쓰지 말고 field_id를 써야 하는 이유는?

A. 쓸데 없이 데이터베이스에 Select 쿼리를 날리기 때문이다. 간단히 다음과 같은 장고 모델이 있다고 하자. 전체 코드는 DRF_CRUD 예제에서 확인할 수 있다. from django.db import models class Category(models.Model): name = models.CharField(max_length=200) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Beverage(models.Model): name = models.CharField(max_length=200) category = models.ForeignKey(Cat..

Q. MySQL 데이터베이스 쿼리 비용Query Cost를 확인하는 방법은?

A. MySQL Workbench를 활용하면 쿼리 비용을 확인할 수 있다! MySQL Workbench에서 번개에 돋보기가 있는 아이콘을 누르면 쿼리 비용을 숫자로 확인할 수 있다. 바로 아래 이미지 아이콘이다. 예를 들어서 아래와 같은 쿼리문의 비용을 확인해보자. SELECT beverage.name, beverage.price FROM beverage INNER JOIN category ON beverage.category_id = category.id; 번개돋보기 아이콘을 누르면 아래처럼 쿼리 비용 1.10이라고 나타나는 것을 확인할 수 있다. 이번에는 LEFT JOIN 예이다. SELECT beverage.name, beverage.price FROM beverage LEFT JOIN catego..

Q. MySQL 인덱스 타는지 안 타는지 확인하는 방법은?

A. Explain을 활용하면 쉽게 확인할 수 있다. 예를 들어서 아래와 같은 쿼리문이 인덱스를 타는지 궁금하다. SELECT beverage.name, beverage.price FROM beverage LEFT JOIN category ON beverage.category_id = category.id; 이때 SELECT 앞에 EXPLAIN만 붙여두면 확인할 수 있다. 아래 예처럼 말이다. EXPLAIN SELECT beverage.name, beverage.price FROM beverage LEFT JOIN category ON beverage.category_id = category.id; 실제 실행해보면 아래 예처럼 결과가 나온다. Extra에 나온 정보를 보면 Using index를 통해서 ..

반응형