heroku로 배포한 사이트 로딩속도가 느린 이유와 해결방법

|

장고를 배우고 처음으로 (아주) 간단한 웹페이지를 pythonanywhere, heroku를 사용하여 배포해보았다.두 서비스 모두 계정당 웹사이트 하나를 무료로 배포할 수 있다. 배포 후 잘 되는 것 처럼 보였는데, 며칠 뒤 접속해보니 첫 페이지 로딩 시간만 5초가 넘게 걸리는 현상이 발생했다.

관련해서 찾아보니 아래 2가지가 원인이고, 특히 2번이 로딩 속도가 느려지는 가장 큰 이유인 것 같다. (자료 :why your first heroku rails app loads so slow)

  1. heroku 서버가 미국에 있다는 점
  2. 방문자가 한동안 (5-15분 사이에) 없으면 사이트가 자동으로 정지되고, 재기동 하는데 20-30초가 걸린다.

이를 해결하는 방법 중 하나는 자신의 heroku 웹사이트에 주기적으로 방문자가 방문하고 있다고 heroku 서버가 착각하게 만드는 것인데, UptimeRobot 같은 모티너링 시스템 등을 활용할 수 있다. 위 시스템을 사용하려면 자신의 heroku app의 IP를 알아야한다. 하지만 heroku app은 하나의 static ip를 갖고 있지 않아서 heroku add-on 등을 사용해야 한다. QuotaGuard Static 라는 add-on을 찾아보았으나 한달에 20달러 정도이고 지역 제한이 있었다.


이틀을 고민한 끝에 AskDjango의 이진석님께 물어보았다.

질문내용

안녕하세요. 배포 관련하여 궁금한 점이 있습니다.

장고로 연습한 사이트를 python anywhere 와 heroku를 사용해서 배포해 보았습니다. 그런데 둘다 며칠 뒤 접속해보니 웹페이지 처음 로딩 속도가 현저히 느려지는 현상이 발생 합니다. python anywhere는 그래도 괜찮은 편인데 heroku 같은 경우는 페이지가 열리기 까지 12초 정도가 걸리네요ㅠ.ㅠ 열심히 정보를 찾아보았는데, heroku 같은 경우 아래가 원인이라는 글을 찾았습니다.

– 방문자가 한동안 (5-15분 사이에) 없으면 사이트가 자동으로 정지되고, 재기동 하는데 20-30초가 걸린다.

이를 해결하는 방법으로 모니터링 시스템 같은 것을 활용해서 자신의 웹사이트에 주기적으로 방문자가 찾아오고 있다고 서버가 착각하게 만들면 된다는 글을 읽었는데요. heroku app은 static ip를 갖지 않아서 모니터링 적용도 쉽지 않아 어떻게 해야하나 이틀 동안 고민하고 있습니다.ㅠㅠㅎㅎ

궁금한 점은 아래와 같습니다.

  1. heroku app 로딩 시간이 10초 이상으로 느려지는건 일반적인가요? (아니면 제 설정이나, 코드에 원인이 있을 가능성이 있을까요?)
  2. 만약 이런 문제가 일반적이고, heroku app 로딩 시간을 개선하는 방법을 혹시 알고 계시다면 의견을 부탁드리고 싶습니다.
  3. 일반적으로 많이 사용하는 (혹은 추천하는) 배포 서비스가 있다면 추천 부탁드리겠습니다.

고맙습니다.

답변 1

  1. 전반적인 로딩 속도 딜레이는 Region 이슈도 있습니다. Free 버전은 대개 미국 Region 에서 서비스를 받습니다.

Heroku의 기본 Plan인 Free Dyno 에서는 sleep 타임이 있구요. Free 버전인만큼 성능상의 제약이 있는 것이 사실입니다. 좀 더 제대로 써보고자 하신다면 유료 플랜을 써보세요. 사이트에 주기적인 접속을 걸기위해 구지 ip를 이용할 필요가 없습니다. 각 heroku app마다 도메인이 할당되니깐요. 도메인으로 접속을 시도하면 됩니다.

Heroku가 PaaS 플랫폼이니 만큼, 초기 구동시간에 다소 시간이 걸리긴 할 겁니다.

답변 2

  1. 맞습니다. 업체 쪽에서 서비스 비용을 줄이기 위해서 사용하는 내부 구조 때문에 그렇구요.
  2. 여러가지 꽁수가 있긴 한데, 기본적으로는 질문하신 데로 주기적으로 누군가 방문하는 방법을 사용하면 되는데 쉽지는 않습니다.
  3. 무료라면 역시 AWS나 애져를 사용하는 방법이 있습니다. AWS에서 일라스틱 빈스톡을 사용하는 방법을 많이 사용하는데, 그냥 ec2에 올려도 됩니다. 개인적으로는 ec2에 올리는 걸 더 선호하는 편입니다. 한 대의 서버를 직접 관리할 수 있기 때문에 서비스 운영이 아닌 공부 용도로는 ec2 서버를 직접 관리하는 걸 추천합니다.

강의노트 02. 튜플, 딕셔너리, 조건문, 반복문, 함수, List Comprehension, File I/O, 예외처리

|

패스트캠퍼스 컴퓨터공학 입문 수업을 듣고 중요한 내용만 간단하게 정리했습니다.

튜플 (tuple)

  • 튜플은 값의 추가, 삭제, 수정이 불가능하다.
  • tuple끼리 연산하는 것은 가능하다. 변수에 담긴 값을 바꾸는 것은 가능하다. (x, y) = (y, x)
  • 따라서 정의시에 값을 같이 정의해야한다. (tuple = (1,2,3,4))
  • list, dic, tuple 정의시 마지막에 ,(콤마)를 넣는게 좋다. (예 : [1,2,3,])
    git은 수정내역을 라인단위로 관리하기 때문에 콤마 때문에 수정내역에 불필요한 한줄이 추가된다.
def quot_and_rem(a,b):
    quot = a // b
    rem = a % b
    return (quot, rem) # 파이썬은 두개 이상의 아웃풋을 낼 수 있다. 괄호가 없어도 튜플로 리턴된다.

(quot, rem) = quot_and_rem(10,3) # 명시적으로 쓰는게 좋다.

딕셔너리 (dictionary)

  • 최근에는 json 형식으로 데이터 교환을 많이 한다. (과거는 xml)
    • node.js
    • mongoDB (nosql의 일종)
  • 딕셔너리에는 index 값이 없다. 따라서 순서도 없다.

예시

# 구조
some_dict = {
	'key':'value',
	'key':'value',
	'key':'value',
	'key':'value',
}

# 예시
member = {
  'user':[
    {
      'id' : 'siwa',
      'name' : 'hyejin',
      'password' : 'asdfjkl123',
      'address' : '',
      'phone' : '',
    },
    {},
    {},
  ]

}

주요함수

dic.keys() # key만 출력
dic.values() # value만 출력
dic.items() # key와 value를 함께 출력
del dic('key') # 요소삭제

nosql / RDB (DBMS 참고)

  • nosql은 key 값으로 바로 value에 접근할 수 있어서 간편하다. record 추가시 원하는 필드 일부만 추가할 수 있다. (관계형데이터베이스(RDB)는 record 추가시 모든 field를 추가한다.)
  • RDB
    • 종류 : MySQL, MariaDB, oracleDB, PostgreSQL(장고와 궁합 좋음), sqlite (기본, 경량, 로컬)
    • 필드 등이 잘 바뀌지 않는 유저정보 같은 데이터 관리에 적합하다.
  • nosql
    • 종류 : mongoDB, Couchbase
    • 상품정보등 휘발성 정보 저장에 좋음, dot notation으로 접근

조건문

  • 파이썬은 객체지향이지만 절차지향이기도 하다.
  • 가장 가능성이 큰 조건, 제일 중요한 조건이 맨 위에 오는게 좋다. (메인프로세스) (중요)
  • 백엔드 개발자는 메모리를 적게 사용하고, 실행문이 짧고, 오고가는 파일의 용량을 최소화 하는데 중점을 둔다. (이미지 요청, 이미지 받기 선택 등)
  • 보통 4칸을 tab 으로 설정해서 사용한다.
  • import 아래에는 일반적으로 2칸을 띄운다.

예시

import random
# 2칸 띄우기

answer = random.randint(1,100)
username = input("Hi there, What's your name??")


while True: # 무한반복
  guess = eval(input("Hi, "+ username + "guess the number: "))
  # eval 함수는 사용자가 입력한 형태 그대로 type을 평가하여 저장한다.

  if guess == answer:
  	print("Correct! The answer was ", str(answer))
    break # 함수종료
  else:
	   print("That's not what I wanted!! Try again")

반복문

  • for 변수 in (리스트 or 문자열)
  • while

Fizzbuzz

num = eval(input("type the number: "))

for i in range(1, num + 1):
	if i % 15 == 0:
		print("fizzbuzz")
	elif i % 3 == 0:
		print("fizz")
	elif i % 5 == 0:
		print("buzz")
	else:
		print(i)

함수

return skill

  • 리턴이 되는 순간 함수는 종료되었다고 인식
  • 금칙어 리스트, id 유효성 체크 등에 많이 사용된다.
def id_check(id):
	if id == "admin":
    	print("invalid id: admin")
    	return # 함수 종료 (반복문에서 break와 동일한 역할)
  print("valid id: ", id)

parameter with initialize

  • 초기값을 설정할땐 항상 그 인자를 마지막에 두어야 한다.
def say_hello(name, nick=True):
	print("Hi, ", name)
	if nick:
		print("But, you are Fool")
	else:
		print("Oh, you are not Fool")

arguments (중요)

  • 파라미터 갯수를 지정하기 힘들 때 *args를 사용한다.
  • *cat 이라고 해도 된다 (이름은 상관 없다)
  • 입력된 다수의 인자를 튜플로 묶는다
def mul_sum(*args):
  print(args) # (args1, args2, args3 ...)
	sum = 0
	for i in args:
		sum += i
	return sum

keyword arguments (중요)

  • key, value로 구성된 다수의 인자를 받을 때 **kwargs를 사용한다.
  • **kwargs는 따로 변수에 담아서 인자로 던질 수 있다.
def kwargs_url(server, **query):
	url = "https://{}?".format(server)
	for key in query.keys():
     url +=  "{}={}".format(key, query[key])
	return url

kwargs_url("www.daum.net", query="pizza", lang="ko")
# 'https://www.daum.net?query=pizzalang=ko'

```python
>>> def test(**kwargs):
...     print(kwargs)

>>> dic = {'a':1, 'b':2}

>>> test(**dic)
    {'a': 1, 'b': 2}
>>> test(a=1, b=2)
    {'a': 1, 'b': 2}

variable outside function

  • 함수는 의존적이면 안된다. 함수 안에서 완결되어야 한다.
  • global은 사용하지 않는게 좋다. 독립성을 잃고 종속적으로 변해서 함수로서의 의미를 잃는다.
a = "hello"
def glob_test():
	global a
	a += "world"
    return a

glob_test(a)
print(a)

List Comprehension

  • Set Comprehension, Dictionary Comprehension 도 존재한다.
  • Dictionary Comprehension 을 사용하면 데이터를 빠르게 처리 할 수 있다.

# List Comprehension
>>> [i * 2 for i in [1,2,3,4,5] if not i % 2 ]
[4, 8]

>>> [i for i in [1,2,3,4,5] if not i % 2]
[2, 4]


# dict comprehensions
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

File I/O

  • 파일의 입출력
입출력 모드의 결정
1. 'write text' 모드
f = open("test.txt", "wt")

2. 'write binary' 모드
f = open("test.bin", "wb")

3. 'read text' 모드
f = open("test.txt", "rt")

4. 'read binary' 모드
 = open("test.bin", "rb")

파일 만들기 (Create New File, Write text)

  • 이미 존재하는 파일을 ‘w’ 쓰기 모드로 열면 기존의 내용을 덮어쓰기를 한다.
file = open('test.txt', 'w')
file.close()

파일내용 추가하기 (Add text)

file = open('test.txt', 'a')
for i in range(1,11):
    text = "line %d.\n" % i
    file.write(text)
f.close()

파일내용 읽기 (Read text)

file = open('test.txt', 'r')
text = file.readline() # 첫 줄만 가져오기
print(text)

texts = file.readlines() # 전체 줄을 리스트로 가져오기
for text in texts:
  print(text)

text = file.read() # 전체 내용을 문자열로 한번에 가져오기
print(text)
f.close()

Error Handler

  • try, except 구문을 활용한다.
  • ValueError는 치명적, NameError 같은 경우는 except: pass 로 넘어가기도 한다.
# ValueError
try:
  input_text = int(input('write a number: '))
except ValueError:
  print('It\'s not a number. please write a number')

# FileNotFoundError
try:
	f = open('error_example.txt', 'r')
except FileNotFoundError as e:
	print(e)
else:
	text = f.read()
    f.close()

# 다수의 에러
try:
  ...
except 오류1:
  ...
except 오류2:
  ...

# 이름을 모르는 모든 에러에 대한 처리
try:
  ...
except:
  ...

# 오류 회피 (에러무시)
try:
    f = open("notExist.txt", 'r')
except FileNotFoundError:
    pass

# 오류 일부러 발생시키기 (에러생성)
if total < 0:
    raise Exception('Total Error')

170330_TIL

|

오늘 한 일

  • 패스트캠퍼스 컴퓨터공학 입문 수업 4일차
    • 파이썬의 간단한 문법 (튜플, 딕셔너리, 함수, 조건문, 반복문)에 대해서 배웠다.
    • 깃헙의 라인단위 수정내역 관리 때문에 딕셔너리나 리스트의 마지막에는 콤마(,) 를 넣는게 좋다.(전부터 궁금한 부분인데 이유를 알 수 있었다.)
    • 함수의 파라미터 중에서 *args, **args 가 항상 헷갈렸는데 수업에서 자세하게 다루어 이제 어느 정도는 이해한 것 같다.
    • 최우영 강사님께서 https://www.hackerrank.com/ 를 두번이나 강추하셨다. 이렇게나 추천 하시는데 사용해봐야겠다.
  • heroku로 배포한 연습사이트의 로딩속도가 느려지는 이유에 대해서 AskDjango의 이진석님께 의견을 들었다. 결론은 유료플랜을 사용해야겠다.
    • 전반적인 로딩 속도 딜레이는 Region 이슈
    • Heroku의 기본 Plan인 Free Dyno 에서는 sleep 타임이 존재
    • 유료 플랜을 사용하면 sleep 타임 제약없이 사용할 수 있음
    • Heroku가 PaaS 플랫폼이니 만큼, 초기 구동시간에 다소 시간이 걸리긴 함
  • AskDjango 강의를 들었다. 템플릿 폴더 아래에 왜 앱 이름으로 폴더를 하나 더 만들어야 하는지에 대한 궁금증이 풀렸다. (중복 방지를 위한 네임스페이스 용도)
    • URL Reverse
    • Template Loader

내일 할 일 (계획)

170329_TIL

|

오늘 한 일

  • git add, commit 단위에 대해서 배웠다. 동작하는 단위에 따라서 최소 단위로 add와 commit을 습관적으로 진행해야 변경사항을 알아보기 쉽고 협업에 편리하다고 한다. 앞으로 연습 프로젝트 진행시에 위와 같은 add commit 패턴을 익혀야겠다.
  • heroku로 패포한 연습사이트 로딩 속도가 느려서 찾아보니 heroku는 5-15분 동안 방문자가 없으면 자동으로 꺼지고 재기동시에 시간에 많이 걸린다고 한다. 이를 해결하는 방법을 찾아서 적용해봐야겠다.
  • 생활코딩 git을 들었다.
  • AskDjango에서 HTML Form, CSRF, 장고 템플릿 상속에 대해서 들었다.

내일 할 일 (계획)

  • 생활코딩 git
  • 패스트캠퍼스 컴퓨터공학 입문 수업 듣기
  • AskDjango 수업 듣기
  • heroku 속도 개선방법 찾아서 적용하기

170328_TIL

|

오늘 한 일

  • 패스트캠퍼스 컴퓨터 공학 입문 수업에서 autoenv라는 아주 유용한 꿀팁을 얻었다. 그동안 매번 장고 프로젝트 작업을 진행할때 virtualenv를 매번 수동으로 activate 하는게 번거롭다고 생각했었다. 이제 pyenv-virtualenv-autoenv 조합으로 프로젝트 디렉토리 접속시 자동으로 가상환경이 실행되도록 할 수 있다. 아직 경험이 부족하여 가상환경의 필요성에 대해서 이해하기는 어렵다. 하지만 그동안 수동으로 진행해서 불편했던 것을 자동으로 바꿔준다는 점에서 autoenv의 필요성은 크게 공감이 간다. 역시 다양하게 어려움을 겪어야 새로운 정보를 얻었을 때 더 잘 받아들일 수 있는 것 같다.
  • heroku를 활용해서 장고 연습프로젝트를 배포해보았다. 장고걸스(djangogirls) 가이드가 큰 도움이 되었다. pythonanywhere와 비교해 보았을 때 heroku 배포 과정이 좀 더 복잡 하다는 생각이 들었다. 하지만 무엇이 좋고 나쁜지는 더 사용해 봐야 알겠다. 현업에서 많이 사용한다는 AWS를 사용한 배포도 해보고 싶다고 생각했다.
  • 생활코딩 git을 들었다.
  • AskDjango 장고 템플릿 상속, Http Status Code에 관한 강의를 들었다. 강의노트는 따로 시간을 내서 정리해야겠다.

내일 할 일 (계획)