1.back_server 폴더 구조
├── Dockerfile
├── README.md
├── access.log
├── app.py
├── auth
│ ├── __init__.py
│ └── user_auth.py
├── config.py
├── managements
│ ├── memory_history
│ ├── memory_monitor.py
│ └── user_generate.py
├── model
│ ├── __init__.py
│ ├── image_model.py
│ ├── room_model.py
│ └── user_model.py
├── requirements.txt
├── service
│ ├── __init__.py
│ ├── image_service.py
│ ├── room_service.py
│ └── user_service.py
├── templates
│ ├── email_auth.html
│ └── search.html
├── test
│ ├── api_performance.py
│ ├── models.py
│ ├── test_model.py
│ ├── test_service.py
│ └── test_view.py
├── test_images
│ ├── sample_image.JPG
│ └── sample_image2.JPG
├── tool
│ ├── __init__.py
│ ├── api.py
│ └── tools.py
└── view
├── __init__.py
├── image_router.py
├── oauth_router.py
├── room_router.py
└── user_router.py
app.py - 메인 어플리케이션 파일
access.log - 로깅 파일(gunicorn실행시 생성)
config.py - 사용자의 기밀 사항이나 민감한 키값들을 저장해 놓는 환경변수
Dockerfile - 도커의 빌드 서술 파일
auth - 사용자의 인증 관련 파일
gitignore - git에서 무시할 파일 이름 저장
model - 데이터베이스와 직접적으로 통신
service - model을 포함하는 상위 단계이고 모델로 불러온 데이터들을 로직처리하여 실제 서비스를 제공
view - service를 포함하는 상위 단계이고 여러 종류의 api들을 라우팅
test - 각종 테스트 코드
tool - 사용자 정의 함수 또는 클래스를 저장
template - html로 내보낼 양식 저장
requirements - app을 실행시키기 위한 패키지 목록
managements - 메모리 모니터링 및 샘플 데이터 생성 등 관리를 위한 툴 파일
2.상세사항
app.py (전체 코드)
class Services:
pass
def create_app(test_config=None):
app=Flask(__name__)
CORS(app)
api=Api(app,title='Image_Us back-server api docs',doc='/api-docs')
if test_config is None:
app.config.from_pyfile("config.py")
else:
app.config.update(test_config)
database=create_engine(app.config['DB_URL'],encoding='utf-8',pool_recycle=app.config['POOL_RECYCLE'],pool_size=app.config['POOL_SIZE'],max_overflow=app.config['MAX_OVERFLOW'])
print("mysql 데이터베이스 연결 성공")
es=Elasticsearch(hosts=app.config['ELASTIC_URL'])
print("elastic 데이터베이스 연결 성공")
@app.before_request
def block_method():
if 'x-real-ip' in request.headers:
ip=request.headers['x-real-ip']
else:
ip=request.environ.get('REMOTE_ADDR')
print("Current IP Address:",ip,flush=True)
print("Current Process:",os.getpid(),flush=True)
splited_ip=ip.split('.')
ip_range=splited_ip[0]+'.'+splited_ip[1]
if ip not in app.config['GOOD_IP_LIST'] and ip_range not in app.config['GOOD_IP_RANGE']:
abort(403)
@api.route("/search")
class search_user(Resource):
def get(self):
headers = {'Content-Type': 'text/html'}
return make_response(render_template('index.html'),200,
headers)
@api.route("/ping")
class ping(Resource):
def get(self):
'''back 작동 테스트를 위한 api입니다.'''
return make_response("pong",200)
user_dao=UserDao(database)
image_dao=ImageDao(database)
room_dao=RoomDao(database)
services=Services
services.user_service=UserService(user_dao,app.config)
services.image_service=ImageService(image_dao,app.config)
services.room_service=RoomService(room_dao,app.config)
oauth_router(api,services,app.config)
user_router(api,services,app.config,es)
room_router(api,services)
image_router(api,services)
return app
app=create_app()
스웨거 적용을 위한 api클래스 생성
api=Api(app,title='Image_Us back-server api docs',doc='/api-docs')
if test_config is None:
app.config.from_pyfile("config.py")
else:
app.config.update(test_config)
database=create_engine(app.config['DB_URL'],encoding='utf-8',pool_recycle=app.config['POOL_RECYCLE'],pool_size=app.config['POOL_SIZE'],max_overflow=app.config['MAX_OVERFLOW'])
print("mysql 데이터베이스 연결 성공")
es=Elasticsearch(hosts=app.config['ELASTIC_URL'])
print("elastic 데이터베이스 연결 성공")
@api.route("/ping")
class ping(Resource):
def get(self):
'''back 작동 테스트를 위한 api입니다.'''
return make_response("pong",200)
user_dao=UserDao(database)
image_dao=ImageDao(database)
room_dao=RoomDao(database)
services=Services
services.user_service=UserService(user_dao,app.config)
services.image_service=ImageService(image_dao,app.config)
services.room_service=RoomService(room_dao,app.config)
oauth_router(api,services,app.config)
user_router(api,services,app.config,es)
room_router(api,services)
image_router(api,services)
db={
"user":"codakcodak",
"password":"test",
"host":"localhost",
"port":3306,
"database":"imageus_back"
}
test_db={
'user':'codakcodak_test',
'password':'test',
'host':'localhost',
'port':3306,
'database':'imageus_back_test'
}
JWT_SECRET_KEY="codakcodak"
DB_URL=f"mysql+mysqlconnector://{db['user']}:{db['password']}@{db['host']}:{db['port']}/{db['database']}?charset=utf8"
test_config={
'DB_URL':f"mysql+mysqlconnector://{test_db['user']}:{test_db['password']}@{test_db['host']}:{test_db['port']}/{test_db['database']}?charset=utf8",
'JWT_SECRET_KEY':"codakcodak"
}
*배포 단계의 db와 개발 단계의 test_db로 환경변수를 나누어 저장
config.py #환경변수파일
wsgi.py #소켓파일
error.log #로그파일
*민감한 파일들을 명시하여 git 상에 무시하고 업로드
*user,room,image의 3개의 모델로 분리
*user,room,image의 3개의 서비스로 분리
*oauth,user,room,image의 3개의 route로 분리
*model,service,view의 계층별 테스트 코드 분리 및 models에는 세 개의 테스크 코드에서 공통적으로 쓸 모델 저장
*사용자 정의 상태코드 및 스웨거의 모델을 생성하는 클래스와 각종 사용자 정의 함수 저장
*이메일 인증 시 보내는 양식 및 각종 html 양식 저장
...
Flask==2.2.2
Flask-BasicAuth==0.2.0
Flask-Cors==3.0.10
flask-restx==1.0.3
Flask-Script==2.0.5
Flask-Twisted==0.1.2
Flask-WTF==1.0.1
gevent==22.10.2
...
*pip 명령어를 통한 app에 쓰인 패키지를 저장
3.장점
1.독립적인 계층구조
view는 service를 포함하고 service는 model을 포함하기에
view를 수정해도 serviced에는 영향이 가지 않는다.
->새로운 기능 및 서비스 생성을 유연하고 빠르게 할 수 있다.
2.계층구조 단의 테스트코드
model,service,view의 층으로 나누어 각각의 테스트 코드를 작성하여 탄탄한 서비스 제공이 가능하다.
->model의 코드 수정이 이루어 졌을 때 service 또는 view에 영향이 가는지를 빠르게 파악할 수 있어 안정적인 서비스 개선이 가능하다.
3.개발 과정별 환경변수 분리
새롭게 생성한 app클래스에 원하는 개발과정 이름을 인자로 넣으면 config 파일에서 매칭되는 변수들을 불러온다.
->각각의 개발 및 배포 과정에서의 필요한 변수들을 쉽게 호출 및 적용하여 개발 환경을 유연하게 변경할 수 있다.