[SK shieldus Rookies 19기] 인프라 활용을 위한 파이썬 8일차와 애자일 특강

기록하는짱구·2024년 3월 11일
0

SK Shieldus Rookies 19기

목록 보기
8/43
post-thumbnail

📌 실습 환경 시작

C:\Users\r2com> cd c:\python\projects\mysite	# 프로젝트 홈 디렉터리로 이동
C:\python\projects\mysite> c:\python\mysite\Scripts\activate  # 가상 환경 활성화
(mysite) C:\python\projects\mysite> code .			# VSCode 실행
(mysite) C:\python\projects\mysite> python manage.py runserver  # 장고 개발 서버 실행

form
HTML 작성 시 사용자의 입력을 서버로 전달

action
사용자의 입력을 어느 페이지, 어느 서버에 전달할 것인지 목적지를 지정

get
사용자가 입력한 정보를 URL에 전달

post
주소가 아닌 요청 본문인 HTTP에 데이터를 담아 전달

redirection
방향 전환, 방향 변경
리다이렉트(redirect, 재지시)를 통해 추가된 내용을 가져감

📌 DTL(Django Template Language)

: https://django-doc-test-kor.readthedocs.io/en/old_master/topics/templates.html#template-inheritance

변수, 필터, 태그, 주석

  • 변수
    : {{ 변수 }}

  • 필터
    : 변수의 값을 특정 형식으로 변환할 때 사용
    : 변수 다음에 | (파이프)를 넣어서 필터를 명시
    : 필터는 : 문자를 통해 인자를 받을 수 있음
    : {{ text | escape | linebreaks }}
    : {{ text | truncatewords:30 }}
    : {{ text | default:"default value" }}
    : {{ text | length }}
    : {{ text | upper }}

  • 태그
    : {% 태그 %}
    : if 문 또는 for 문과 처럼 흐름을 제어하기 위해 사용
    : {% extends %}와 같이 단독으로 사용하는 템플릿 태그도 있고, {% if %} {% endif %} 처럼 반듯이 닫아줘야 하는 템플릿 태그도 있음

  • 주석
    : 주석 처리 시 사용
    : {# 한 줄 주석 #}
    : {% comment %}
    : 여러 줄 주석
    : {% endcomment %}

def index(request):
    question_list = Question.objects.order_by('-create_date')
    context = { 'question_list' : question_list }
    
    return render(request, 'pybo/question_list.html', context)



{% if question_list %}
    <ul>
        {% for question in question_list %}	 # 뷰의 context 변수에 정의한 키 이름 
            <li><a href="/pybo/{{ question.id }}/">{{ question.subject }}</a></li>
        {% endfor %}
    </ul>
{% else %}
    <p>질문이 없습니다.</p>
{% endif %}

📌 질문 상세 기능 구현

질문 목록 중 하나를 클릭했을 때 오류가 발생하는 것을 확인

/pybo/숫자/ 형식의 URL 패턴이 정의되어 있지 않음

pybo\urls.py 파일에 패턴 추가

from django.urls import path
from . import views         # 현재 패키지에서 views 모듈을 가져옴

urlpatterns = [
    path('', views.index),
    path('<int:question_id>/', views.detail),
]

pybo\views.py 파일에 detail 메서드 추가

from django.shortcuts import render
from .models import Question

def index(request):
    question_list = Question.objects.order_by('-create_date')
    context = { 'question_list': question_list }
    
    return render(request, 'pybo/question_list.html', context)


def detail(request, question_id): 
    question = Question.objects.get(id=question_id)
    context = { 'question': question }

    return render(request, 'pybo/question_detail.html', context)

templates\pybo\question_detail.html 템플릿 정의

<h1>{{ question.subject }}</h1>

<div>
	{{ question.content }}
</div>

브라우저를 통해 확인

📌 get_object_or_404 메서드 활용

존재하지 않는 데이터를 조회하는 오류가 발생

오류 메시지에 시스템(프로그램) 내부 구조 및 로직이 포함되어 출력
→ 오류 메시지를 통해 정보를 수집해 추가 공격을 계획

get 메서드를 get_objct_or_404 메서드로 대체

모델의 기본키를 이용해서 모델 객체 한 것을 반환

from django.shortcuts import render, get_object_or_404
from .models import Question

def index(request):
    question_list = Question.objects.order_by('-create_date')
    context = { 'question_list': question_list }
    
    return render(request, 'pybo/question_list.html', context)

def detail(request, question_id): 
    # question = Question.objects.get(id=question_id)
    question = get_object_or_404(Question, pk=question_id)	
    context = { 'question': question }

    return render(request, 'pybo/question_detail.html', context)

존재하는 데이터를 조회하는 경우

존재하지 않는 데이터를 조회하는 경우

: 내부 정보가 포함되지 않는 단순한 메시지가 출력

📌 URL 매핑 정보에 별칭 사용

question_list.html 템플릿

<li><a href="/pybo/{{ question.id }}/">{{ question.subject }}</a></li>
             ~~~~~~~~~~~~~~~~~~~~~~~~
             질문 목록에서 제목(링크)을 클릭했을 때 이동하는 주소 
             → 주소가 하드 코딩되어 있음
             → 변경을 일관되고 쉽게 반영하기가 어려움

pybo\urls.py 파일에 URL 별칭 추가

from django.urls import path
from . import views

urlpatterns = [

    # /pybo/ 형식의 주소에 index라는 이름 부여
    path('', views.index, name='index'),
    
    # /pybo/숫자/ 형식의 주소에 detail라는 이름 부여
    path('<int:question_id>/', views.detail, name="detail"),
]

templates\pybo\question_list.html 템플릿 파일에 URL 별칭을 사용하도록 수정

{% if question_list %}
    <ul>
        {% for question in question_list %}
            {# <li><a href="/pybo/{{ question.id }}/">{{ question.subject }}</a></li> #}
            <li><a href="{% url 'detail' question.id %}">{{ question.subject }}</a></li>
        {% endfor %}
    </ul>
{% else %}
    <p>질문이 없습니다.</p>
{% endif %}

URL 네임스페이스 적용

서로 다른 앱에서 같은 URL 별칭을 사용하면 중복 문제 발생
→ 네임스페이스(namespace: 각각의 앱이 관리하는 독립된 이름 공간)를 적용해서 해결 가능

pybo\urls.py 파일에 네임스페이스 이름을 값으로 가지는 app_name 변수 추가

from django.urls import path
from . import views

app_name = 'pybo'

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:question_id>/', views.detail, name="detail"),
]

템플릿에 사용된 별칭의 네임스페이스를 정의하지 않아서 오류 발생

templats\pybo\question_list.html 파일에서 사용하고 있는 별칭의 네임스페이스 지정

{% if question_list %}
    <ul>
        {% for question in question_list %}
            {# <li><a href="/pybo/{{ question.id }}/">{{ question.subject }}</a></li> #}
            <li><a href="{% url 'pybo:detail' question.id %}">{{ question.subject }}</a></li>
        {% endfor %}
    </ul>
{% else %}
    <p>질문이 없습니다.</p>
{% endif %}

정상적으로 동작하는 것을 확인

📌 답변 저장 및 출력 기능 추가

질문 상세 페이지(templates\pybo\question_detail.html)에 답변 등록 버튼 추가

<h1>{{ question.subject }}</h1>

<div>
	{{ question.content }}
</div>

<form action="{% url 'pybo:answer_create' question.id %}" method="post">
	{% csrf_token %}
	<textarea name="content" id="content" rows="15"></textarea>
	<input type="submit" value="답변 등록"/>
</form>

상세 페이지 확인

URL 맵핑이 정의되지 않아 오류가 발생

pybo\urls.py 파일에 URL 맵핑 등록

from django.urls import path
from . import views

app_name = 'pybo'

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:question_id>/', views.detail, name="detail"),
    path('answer/create/<int:question_id>', views.answer_create, name='answer_create'),
]

pybo\views.py 파일에 answer_create 메서드 추가

from django.shortcuts import render, get_object_or_404, redirect
from .models import Question
from django.utils import timezone

def index(request):
    question_list = Question.objects.order_by('-create_date')
    context = { 'question_list': question_list }
    
    return render(request, 'pybo/question_list.html', context)

def detail(request, question_id): 
    # question = Question.objects.get(id=question_id)
    question = get_object_or_404(Question, pk=question_id)
    context = { 'question': question }

    return render(request, 'pybo/question_detail.html', context)

def answer_create(request, question_id):

	# 답변을 추가할 질문을 조회
    question = get_object_or_404(Question, pk=question_id)	
    question.answer_set.create(content=request.POST.get('content'), create_date=timezone.now())

	# 답변 추가 후 상세 페이지로 리다이렉트
    return redirect('pybo:detail', question_id=question.id)	

templates\pybo\question_detail.html 파일에 답변을 출력하도록 수정

<h1>{{ question.subject }}</h1>

<div>
	{{ question.content }}
</div>

{# 답변 내용을 출력 #}
<h5>{{ question.answer_set.count }}개의 답변이 있습니다.</h5>
<div>
	<ul>
		{% for answer in question.answer_set.all %}
			<li>{{ answer.content }}</li>
		{% endfor %}
	</ul>
</div>

<form action="{% url 'pybo:answer_create' question.id %}" method="post">
	{% csrf_token %}
	<textarea name="content" id="content" rows="15"></textarea>
	<input type="submit" value="답변 등록"/>
</form>

브라우저를 통해 확인

📌 스타일 적용

스태틱 디렉터리를 생성하고 해당 위치를 설정 파일에 추가

C:\python\project\mysite\static\style.css 파일 생성


						  # 선택자 { 적용할 스타일; 적용할 스타일; ... } 
							→ https://poiemaweb.com/css3-selector 참고 

textarea {				  # 요소(태그) 선택자 → textarea 태그에 적용할 스타일 지정
	width: 100%;
}

input[type=submit] {      # 속성 선택자 → input 태그 중
	margin-top: 10px;	    type 속성의 값이 submit인 태그에 적용할 스타일 지정
					              
}

config\settings.py 파일에 스태틱 디렉터리 위치 등록

STATIC_URL = '/static/'

STATICFILES_DIRS = [
	BASE_DIR / 'static', 
]

질문 상세 페이지(templates\pybo\question_detail.html)에 스타일 적용

{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'style.css' %}">

<h1>{{ question.subject }}</h1>

<div>
	{{ question.content }}
</div>

{# 답변 내용을 출력 #}
<h5>{{ question.answer_set.count }}개의 답변이 있습니다.</h5>
<div>
	<ul>
		{% for answer in question.answer_set.all %}
			<li>{{ answer.content }}</li>
		{% endfor %}
	</ul>
</div>

<form action="{% url 'pybo:answer_create' question.id %}" method="post">
	{% csrf_token %}
	<textarea name="content" id="content" rows="15"></textarea>
	<input type="submit" value="답변 등록"/>
</form>

브라우저를 통해 스타일이 적용된 것을 확인

질문 내용을 입력하는 창(textarea 태그)의 넓이가 화면의 가로 100%를 차지하고 답변 등록 버튼(submit 버튼)의 윗쪽 바깥 여백이 10px 추가됐는지 확인

📌 부트스트랩 적용

부트스트랩 4.5.3 다운로드

https://getbootstrap.com/docs/4.5/getting-started/download/

다운로드한 압축파일을 압축 해제한 후 bootstrap.min.css 파일만 static 디렉터리로 복사

질문 목록 템플릿(templates\pybo\question_list.html)에 부트스트랩 적용

{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap.min.css' %}" >

{% if question_list %}
    <ul>
        {% for question in question_list %}
            {# <li><a href="/pybo/{{ question.id }}/">{{ question.subject }}</a></li> #}
            <li><a href="{% url 'pybo:detail' question.id %}">{{ question.subject }}</a></li>
        {% endfor %}
    </ul>
{% else %}
    <p>질문이 없습니다.</p>
{% endif %}

질문 목록 템플릿(templates\pybo\question_list.html)에 부트스트랩 스타일을 적용

{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap.min.css' %}" >

<div class="container my-3">
    <table class="table">
        <thead>
            <tr class="thead-dark">
                <th>번호</th>
                <th>제목</th>
                <th>작성일시</th>
            </tr>
        </thead>
        <tbody>
            {% if question_list %}
                {% for question in question_list %}
                    <tr>
                        <td>{{ forloop.counter }}</td>
                        <td>
                            <a href="{% url 'pybo:detail' question.id %}">
                            {{ question.subject }}
                            </a>
                        </td>
                        <td>{{ question.create_date }}</td>
                    </tr>
                {% endfor %}
            {% else %}
                <tr>
                    <td colspan="3">질문이 없습니다.</td>
                </tr>
            {% endif %}
        </tbody>
    </table>
</div>

브라우저를 통해 결과 확인


📌 Agile 특강

Agile에 대한 이해

협업, 분업, 의사소통을 위한 방법론

  • 협업에 대한 이해

: 협업의 표면적인 이유
→ 상호작용, 분업, 빠른 처리가 가능하기 때문

: 협업의 본질적인 이유
→ "Schedule" # 빨라진 서비스 출시 일정
→ 서비스 하나 출시까지 평균적으로 약 4개월 정도 소요
→ 빠른 출시를 위한 업부 방법으로 협업이 있었던 것
→ 협업 시작 시 가장 먼저 하는 단계가 '요구사항 분석' 단계

  • 폭포수 개발 방법론(waterfall) 방식
    : 이전 단계가 끝날 때까지 다음 단계를 실행하지 않고 대기

  • 절차

    요구사항 분석 → 프로그램 설계 → 구현/코딩 → 테스팅 → 유지·보수

  • 장점

  1. Simple
    : 단순한 선형 모델

  2. Systematic
    : 단계 별 정형화 된 접근

  3. Clarity
    : 명확한 진행 상황 파악

요즘은 에자일을 많이 쓴다고는 하지만 막상 파헤쳐보면 폭포수 방식을 많이 사용

  • 단점
  1. Parallel
    : 각 단계 병행이 불가능

  2. Issue Check
    : 중요 경함에 대한 대응이 어려움

  3. Flexible
    : 떨어지는 유연성

  • 속도를 내기 위해선 다른 대안이 필요
    → Agile(날렵한, 민첩한) 방법론
    → 기민한(민첩한) Software Development
    → 이에 기인한 것이 애자일 방법론(Agile Software Development)

  • 애자일 선언문

    공정과 도구보다 "개인과 상호작용"
    포괄적인 문서보다 "작동하는 소프트웨어"
    계약 협상보다 "고객과의 협력"
    계획을 따르기 보다 "변화에 대응하기"

→ 즉시 사용자의 피드백을 받을 수 있는 상황이 조성되어 보다 빠른 개발 가능

애자일 방법론

: XP, DevOps, DaD, Lean Startup, Scrum, Kaizen, Product Development 등

  • 스크럼(Scrum)
    : 미식 축구에서 따온 용어(미식 축구처럼 6~7명씩 모여 팀을 꾸리기 때문)
    : 스크럼 프레임워크(Scrum Framework)
    : 민첩한 개발을 위한 방법론 중 하나

절차
Product Backlog → Sprint Planning → Sprint Backlog → Daily Scrum → Increment → Sprint Review → Sprint Retrospective

  1. Product Backlog
    : 프로젝트 이해도가 제일 높은 사람이 많이 작성 (>> 프로젝트 매니저)
    : 시간이 가장 오래 걸리는 작업
    : 추상적으로 크게 작성하기 보다는 자잘하게 작성하는 것이 좋음
    : 더 이상 작성할 게 없을 때까지 작성

  2. Sprint Planning
    : 스프린트 기간은 보통 2주(3주는 너무 길고 보통은 1~2주)

  3. Sprint Backlog
    : 스프린트 기간에 수행되어야 하는 업무의 목록
    : Product Backlog에서 해야 할 일을 선택해서 수행

  4. Scrum Team
    : 개발팀과 검증(테스트)팀, 매니징팀으로 구성
    : 스크럼 팀을 구성할 때 제일 중요한 룰(Daily Scrum)
    : 어제 한 일과 오늘 할 일을 매일 15분 정도로 짧게 팀에게 공유하고 회의하여 진척 사항을 검토

  5. Increment
    : 스프린트 프로덕트에서 새로 업데이트 되고 새로운 요소들이 증가된 산출물

  6. Sprint Review
    : 스프린트 종료 후, 구현된 산출물을 Review
    : 잘 완수하지 못했거나 좀 미숙한 부분들은 Proudct Backlog에 쌓아놓음

  7. Sprint Retrospective
    : 다 같이 모여서 개선할 부분, 리스크가 있는 부분 등에 대해 복귀
    : 개선점이나 미숙한 부분들을 반영해 Sprint Planning에 대해 쌓아두어 문제 개선

  • 스크럼에서 제일 중요한 건 회고(Retrospective)
    → 회고를 하지 않고 계속 넘어간다면 어느 부분이 부족하고 어느 부분이 개선이 필요한 지에 대해 알기 어려워 팀의 발전이 어려움

데브옵스(DevOps)

: Development + Operate
: 개발과 운영을 하나로 합쳐놓은 합성어

  • DevOps가 필요한 이유
    : 과거, 개발자와 운영자의 관점은 전혀 달랐음
    : 이러한 관점의 차이에 대한 간극을 줄여줄 만한 것이 필요
    → DevOps

  • DevOps Tools
    : 산업에서 사용되는 툴들

  1. Plan
    : 일의 진행 상태를 확인
    : ex. draw.io, Google Drive, Trello

  2. Build
    : 컨테이너 같은 내용물을 저장하고 관리
    : ex. git, kubernetes, docker, GitHub

  3. Continuous Intergration
    : 지속적인 통합
    : ex. Jenkins, circleci, Github Actions, Travis CI

  4. Deploy
    : 배포로 인한 장애 요인 제거
    : ex. argo(아르고), Google Cloud Platform, Microsoft Azure, amazon web service(AWS)

  5. Operate
    : 운영 관점
    : ex. slack, New Relic, DATADOG, SENTR

회고

: 업무의 효율성을 증진시키기 위해 현업에서 많이 사용하는 방식

  • 두 가지 방식의 회고
  1. 미래형 회고
    : 미래도 같이 예측
    → 초기 팀의 스타트, 처음 스프린트 시 좋음

  2. 현재형 회고
    : 현재 수행한 내용만 집중
    → 스프린트를 많이 돌린 상태라면 현재형이 좋음

  • 회고의 종류
  1. GIST
    : 단점이라면 현재를 아예 돌아보지 않기에 회고에 대해서는 살짝 아쉬움
    : Goals, Ideas, Steps, Tasks
  • Goal
    : 목표
  • Idea
    : 목표에 도달하기 위한 기획
  • Step
    : 절차
  • Task
    : 스텝을 수행하기 위한 업무
  1. Now, Next, Later
  • 현재는 Now,
  • 우리가 그 다음 해야하는 것을 Next,
  • 그 다음에 해볼만한 것은 Later에 기입
  1. Hot-air Balloon - Bad Weather
    : 열기구 - 날씨 패턴
  • 프로젝트를 진행하며 좋았던 점 등은 열기구 위에 작성
  • 프로젝트를 진행하면서 아쉬운 점, 프로젝트 지연 요소 등은 열기구 하단에 작성
  • 프로젝트의 긍정적 요소
    → sunny Days(왼쪽)
  • 프로젝트의 리스크, 부정적 요소
    → Storm(오른쪽)
  1. 3L 패턴
    : Liked, Learned, Lacked
  • 칭찬할만한 것 : Liked
  • 프로젝트를 하며 배웠던 점 : Learned
  • 부족했거나 아쉬웠던 부분 : Lacked
  1. Open the box
    : 프로젝트 초창기 스프린트 회고 시에 많이 사용
  • 새로운 아이디어, 재활용 했으면 하는 아이디어, 버리면 했으면 하는 아이디어 기입
  1. Speed Car
    : 현재만 집중하는 패턴
    : 가장 단순하기에 처음 접했을 때 쉽게 접근하기가 가장 좋음
  • 차의 엔진 쪽에 긍정적 영향 기입
  • 차의 낙하산 쪽에 부정적 영향 기입
  1. 4 Ls
    : 3 Ls에 Longed for 추가
    : Lacked에 아쉬운 점을 적는 거라면 Longed for에는 바램(소원) 같은 걸 기입
  • 칭찬할만한 것 : Liked
  • 프로젝트를 하며 배웠던 점 : Learned
  • 부족했거나 아쉬웠던 부분 : Lacked
  • 프로젝트에 바라는 점 : Longed for

0개의 댓글