(* 참조 - https://sploitfun.wordpress.com/2015/02/11/syscalls-used-by-malloc/)


malloc은 메모리 할당을 위해 사용하는 함수이다. 그 내부에서는 실제로 메모리를 할당받기 위해 시스템콜을 사용한다.

두가지 brk와 mmap 시스템콜을 사용한다.


(malloc)


brk
: brk는 프로그램 break location을 증가시키므로써 메모리를 획득한다. 

brk 를 확인해보기 위해 아래와 같이 코드를 작성한다.


(brk 테스트)


코드에서 brk를 사용했는데, sbrk는 어떤 목적으로 사용하였을까?



(brk 메뉴얼)


brk 메뉴얼을 확인해보면, brk와 sbrk가 있다.

sbrk의 경우 인자 값으로 확장하고 싶은 사이즈를 적고, 리턴값으로는 확장되기 이전의 program break를 반환한다. 그렇기에 저 위의 코드 sbrk(0)를 사용하게 되면 확장 사이즈는 0이므로 program break의 변화는 없고, 이전(변화가 없으니 현재) break location을 알 수 있다.

실행한 결과를 확인해보자.



(brk 되기 전)


brk 되기 전 프로그램 Break Location의 주소이다.

실제 메모리 모습을 확인해보면


(메모리 모습(brk 전))


[heap] 영역의 끝이 프로그램 Break Location인 것을 확인 할 수 있다.

brk를 한 후 결과를 확인해본다.



(brk 후)


brk 후 프로그램 Break Location의 위치가 0x1000 늘어난 것을 확인 할 수 있다.

메모리 상황을 확인해보면


(메모리 (brk 후))


[heap]의 영역이 늘어난 것을 확인 할 수 있다.

마지막으로 brk를 원래 위치로 되돌리는 것 까지 확인해보자.


(원위치)



(메모리 상황)


brk를 이용해 다시 줄여보았다. 이로써 brk를 통해 메모리를 할당하는 방식을 알아보았다.

그 다음 방식은 mmap이다.
mmap
: mmap은 호출한 프로그램이 사용하고 있는 영역에 대해 메모리를 잡는 것이다. 

코드를 이용해 직접 확인해보자.



(테스트 코드)


mmap 되기 전 상황과 된 후 상황일 먼저 비교해볼 것이다.


(mmap 전)



(메모리 (mmap 전))


mmap 전 라이브러리 파일 영역이다.

그 후 mmap을 진행한다.



(mmap 실행)



(메모리 ( mmap 후))


기존에 있던 프로그램의 메모리 영역에 mmap을 통해 공간을 만든 것을 확인 할 수 있다.

munmap 후까지 확인해본다.



(munmap)



(메모리 ( munmap 후 ))


munmap 후 다시 프로그램에게 메모리가 반환되는 것을 확인 할 수 있었다.

'Hacking > System Hacking' 카테고리의 다른 글

(RootKit) Simple Rootkit  (0) 2018.04.12
(RootKit) LKM(Loadable Kernel Module)  (0) 2018.04.03
System Hacking - jmp (반복문)  (0) 2017.04.19
System Hacking - jmp (분기문)  (0) 2017.04.18
System Hacking - 어셈블리어(사칙연산)  (0) 2017.04.14
  오늘 분석한 how2heap 자료는 poison null byte 이다.
poison null byte는 heap 영역 뿐아니라 굉장히 다양한 곳에서 사용할 수 있는 개념이다.

이 취약점은 off-by-one 에러를 이용한다.
off-by-one 이란 한바이트 혹은 두바이트 정도의 overflow를 허용하므로써 취약점으로 연결 시키는 방식을 말한다.
보통 overflow 공격에서 잘 막아두는데, for 문이나 코딩을 하다가 간혹 한바이트 정도를 더 받게 되는 경우가 있다. 대표적으로 string이다. 문자열을 입력받으면 맨 마지막에 null이 따라온다.

예를 들어 char buf[10] 이라고 하자.
여기에 우리가 overflow 개념이 있어서 이것을 막고자 글자수를 10자만큼만 받겠어! 라고 조건문을 걸었다. 그렇다면 15글자 등등 10글자를 넘는 조건은 우리가 막을 수 있다. 하지만 딱 10글자가 들어온다면? -> 뭐 괜찮지라고 생각할 수 있지만, 문자열은 맨 마지막에 null이 따라온다.
즉, 10글자 string을 입력받게 되면 맨 우리의 buf에서 한바이트가 overflow가 일어난다.
한바이트 overflow정도야 라고 생각하겠지만 이것이 return 주소의 한바이트를 덮어쓰므로써 return 주소를 조작할 수 있고 큰 취약점으로 연결 될 수 있다.

off-by-one 개념은 여기까지 간단히 알아보고
poison null byte 자료를 분석해보자.

이 자료를 이해하는데 어려웠다. 이 포스트 글을 끝까지 읽어보면 아다리가 맞듯 이해가 될 것이다.

결과적으로 이 자료에서는 중복된 영역의 heap 주소를 할당받았다.
할당 받은 heap 영역 안에 기존에 이미 할당받았던 heap 영역이 있어서 덮어쓸 수 있는 상황으로 만들었다. 그렇다면 시작! 해보자!

malloc으로 시작한다.



(malloc)


0x100 사이즈를 할당 받는다.

(실제 사용 가능한 사이즈)


실제 사용 가능한 사이즈는 어떻게 될까?



(실제 사용 가능한 사이즈)


실제 사용 가능한 사이즈는 0x8 바이트 더 쓸수 있게 나와있는데, 이는 이전 포스트 글에서 설명했으므로 넘어갈 것이다. ( malloc의 사용가능영역 이라는 글에 정리해 두었다. )



(추가 malloc)


그 후 두 개의 영역을 더 할당한다.

위의 과정을 마치면 크게
a ( 0x100)
b ( 0x200)
c ( 0x100)
의 모양으로 heap영역에 할당 되어있다.

여기서 b 영역의 사이즈를 확인해보기 위해 포인터를 이용했다.



(b chunk size 포인터)



(gdb 메모리 확인)


gdb로 메모리를 확인해보면 저 위치는 위와 같다.
우리는 0x200을 할당했는데? 0x211?
사이즈(0x200) + 헤더 크기(0x10) | flag
의 결과이다.

원래 자료에서는 b 영역 안에 0x200의 크기를 적어주는 부분이 있는데 순서를 바꾸어서 설명하겠다. 그것이 더 이해하기 편할거 같다.

그 후 b를 free해준다.



(free(b))


그렇게 되면 b 영역은 free 된 chunk가 되고

c chunk의 header에서 PREV_SIZE 영역에 이전 chunk가 b이므로 b가 해제된 순간 b의 사이즈가 c chunk header에 적히게 된다.



(PREV_SIZE)


이전 청크의 사이즈인 0x210이 적히게 된다.

그 다음에 할 작업의 목적을 이해해야한다.
우리는 지금 작성된 0x210, c chunk에 있는 PREV_SIZE를 건들이고 싶지 않다.
그대로 잘 0x210으로 보존하고 싶다. ( 그 이유는 끝까지 보면 이해가 될 것이다. 지금은 아 그래야하는 구나라고 생각하고 쭉 읽어보자. )

그렇기에 우리는 free된 b chunk의 사이즈영역에 overwrite를 하여 0x210을 0x200으로 만들것이다.(a chunk에서 덮어써서, 한바이트 overflow로)

후에 unlink가 일어나는데, 이 때 검사하는 조건이 있다.

chunksize(P) != prev_size(next_chunk(P))

우리는 b의 헤더에서 한바이트 덮어써서 0x210을 0x200으로 만들 것이다.
그렇다면 지금 b주소 + 0x200 에 들어있는 0x00 값이 있는데 위의 조건에 걸려서 에러가 난다.

그렇기에 b주소 + 0x200 의 위치에 0x200 을 적어줘야한다.



(check 우회)


b의 주소는 헤더를 넘어간 주소이므로 0x1f0을 더한 값이 b chunk 주소 + 0x200 의 위치가 된다.

해당 위치에 0x200을 넣는다.



(확인)


확인해보면 현재 메모리에 200이 들어가 있는 것을 확인 할 수 있다.

그리고 b chunk 헤더에 있는 chunk size를 확인해보면



(b chunk size)


현재 0x211 크기이다.

이제 여기서 한바이트를 덮어쓰면



(덮어쓰기)


여기서는 편하게 배열의 인덱스로 접근하여 적었다. 실제에서는 문자열을 이용해 null을 입력한다.



(덮어쓴 모습)


덮어쓰면 b chunk size가 0x200이 된다.

이제 여기서 b1에 다시 malloc 한다.

아까 b chunk 크기보다 작은 크기로



(malloc)


그렇게 되면 아까 free된 b chunk의 주소가 할당된다. 이 과정에서 unlink가 일어나게 된다.
여기서 아까 위에서 우리가 우회한 조건을 검사하고
해당 PREV_SIZE 위치(우리가 조작한) 에 값이 0x200이었는데
우리가 0x100을 요청했으므로 실제 청크 사이즈는 0x110이고
이 값을 뺀 값!
0x200 - 0x110 = 0xf0

이 값이 우리가 조작한 PREV_SIZE 위치에 적히게 된다.


(조작된 PREV_SIZE)


그 후 b2를 또 다시 malloc 한다. 이번에는 0x100보다 작은 사이즈를 할당해야한다. 그래야 아까 사용하던 b chunk를 사용하기 때문이다.

b1이 쓰고 남은 그 뒤를 할당받는다.



(b2 malloc)


그렇게 되면 현재 상황은
a ( 0x100 )
b1 ( 0x100 )
b2( 0x80 )
c ( 0x100 )
이 된다.

여기서 공격대상의 chunk는 b2이다.

b2의 내용이 원래 B로 채워진 값들이었다고 치자.



(B로 채우기)



(현재 b2의 값)


현재 b2의 값은 위와 같다.

여기서 b1과 c를 free한다.



(free)


여기가 핵심이고 하이라이트다.

b1을 free한다.
그렇게 되면 b1은 free chunk가 된다.
그리고 c를 free할 때 사건이 일어난다.
c를 free하면 c의 PREV_SIZE를 확인하는데 이 때 0x210으로 적혀져 있다. (지금 이 상황을 만들기 위해 아까 이 값을 보존하고 싶었던 것이다.)
그렇기에 free를 할 때 c chunk의 이전 청크 사이즈인 0x210을 고려하여
c chunk주소 - 0x210 의 주소에 가봐서 free인지 확인한다.
확인해보니. 얼래! 아까 우리가 b1을 free했기 때문에 free 된 영역이다.
그렇기에 glibc는 안타깝게도 b2를 보지 못하고 b1과 c를 병합하게 된다.
그리고 그 주소를 freelist에 넘기게 된다.

그 후 다시 malloc을 통해 0x300( 원래 b 사이즈 + 원래 c 사이즈)의 크기로 요청을 한다면



(malloc)


그렇게 되면 freelist에 있는 병합된 방금 우리가 free한 b1의 주소를 받게 된다.
사이즈는 0x300만큼
그렇게 되면
d가 할당받은 영역은 b2를 포함하고 있게 된 것이다.
그러면 d를 D 라는 문자로 덮어쓰고

그 후에 b2의 값을 확인해보면?



(b2의 내용)


b2의 내용은 아까 B로 가득차 있었는데,
D로 가득찬 모습을 확인할 수 있게 된다.

즉, 다른 chunk안에 있는 내용을 조작할 수 있다는 것이다.

이것이 구조체여서 함수값을 조작할 수도 있고 가능성은 넓다.

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

(how2heap) - overlapping_chunks  (0) 2018.02.22
(how2heap) - house of lore  (0) 2018.02.21
malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
(how2heap) - unsafe_unlink  (0) 2018.01.13
  우리는 malloc을 이용해 heap영역의 메모리 주소를 할당 받는다.
우리가 할당받은 주소는 heap영역의 chunk(덩어리)이다. 실제 덩어리에서 우리가 사용할 수 있는 공간은 어느정도 일까?

간단하게 malloc으로 0x10과 0x18 사이즈로 두개를 할당받는 코드를 만들었다.


(테스트코드)


그리고 malloc_usable_size 함수로 사용가능한 크기를 확인해보았다.



(확인 결과)


확인 결과 두개의 주소 모두 0x18로 나왔다. 어떻게 된것일까?

gdb를 통해 메모리 모습을 확인해보았다.



(메모리 모습)


우리는 분명 0x10과 0x18의 크기로 메모리 할당을 요청했음에도 불구하고 실제로 chunk 사이즈를 보면 0x21로 두개의 chunk가 할당된 것을 볼 수 있다.
여기 나와있는 사이즈는 chunk의 실제 크기이다.

0x10으로 할당을 하면 위의 예시에서 6010 주소가 반환된다. 이 chunk의 헤더는 0x10크기를 가진다.
그렇기에 실제 chunk의 크기는 0x10 큰 사이즈이다. 그렇다면 0x20이 아닌 0x21인 이유는 마지막 3비트는 flag비트로 사용되기 때문이다.

다시 글의 주제로 돌아가서 실제 사용가능한 크기가 전부 0x18이라니.
이 이유는 우리가 0x10을 요청했을 경우 실제로 0x10크기의 영역이 잡힌다. 그 후에 다음 청크가 할당되었는데 이 때 다음 청크의 헤더 앞부분까지 실제로는 사용가능하다. 위 사진에서 빨간색 테두리만큼 실제로 데이터를 쓰기가 가능하다는 것이다.

이해하기 쉽게 정리하면 청크의 헤더는 0x10크기이다.
그 중 0x8 크기는 자신의 chunk 사이즈를 담고있고 그 앞의 0x8바이트는 
PREV_SIZE 영역으로 이전 chunk의 크기를 가지고 있는데, 
-> 이전 chunk가 사용중이라면 이 영역은 이전 chunk가 사용한다.
-> 이전 chunk가 사용중이지 않다면(free됬다면) 이 영역은 이전 chunk의 size를 가지고 있게 된다.
그렇기에 실제로 사용가능한 크기는 다음 chunk의 헤더 앞부분 0x8크기만큼을 더 쓸 수 있다는 이야기이다.

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

(how2heap) - house of lore  (0) 2018.02.21
(how2heap) - poison null byte  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
(how2heap) - unsafe_unlink  (0) 2018.01.13
(how2heap) - fastbin_dup_into_stack  (0) 2018.01.11

GNU의 C에서 함수 혹은 구조체, 변수등에 컴파일러가 코드를 컴파일 할 때 특정 방향으로 컴파일 할 수 있도록 속성을 설정할 수 있는 것이 __attribute__ 이다.

GNU의 C 코드에서 간혹 __attribute__ 라고 써있는 것을 볼 수 있다.
_ 2개를 attribute 끝에 붙여서 사용한다. 그리고 그 뒤에 괄호 두개 사이에 원하는 속성을 넣으면 된다.

예로 이렇게 사용한다.
내가 how2heap의 house of spirit을 분석하다가 마주한 코드이다.

unsigned long long fake_chunks[10] __attribute__ ((aligned (16)));

위와 같이 사용한다.
사용할 수 있는 속성은 굉장히 많다.

예로 든 aligned 속성은 메모리의 정렬 단위를 설정하는 것이다.
해당 내용은 memory alignment를 검색해보면 찾아 볼 수 있다.

간단히 설명하면 메모리에서 입출력을 할 때 가져오는 단위 WORD 로 가져오기 때문에 메모리에 값을 저장할 때 신경을 써야한다. 
예를 들어서 좋지 않은 경우를 보겠다.

32비트 컴퓨터라고 하자. 그렇다면 WORD의 단위는 4바이트이다. 그렇다면 메모리에서 4바이트 단위로 값을 가져온다.
여기서 우리가 int 의 값을 저장한다고 하자.( int 는 4바이트(word의 값))
메모리 주소 0번지 부터 저장한다 치면 주소 0,1,2,3 에 우리의 값이 적혀지게 되고
이 값을 부를 때 0번지 주소를 부르게 되면 4바이트 단위로 값을 가져오기 때문에 0,1,2,3 주소의 값을 가져오게 되어 한번의 호출로 우리가 저장한 값을 가져올 수 있다.

하지만 만약 2번지 주소에 저장되었다 치자.
그러면 2,3,4,5 주소에 우리의 int 값이 저장된다. 이 값을 호출 하기위해 메모리 주소를 2번부터 부를 수 있을까? 그렇지 않다. 32비트의 컴퓨터이므로 4바이트 단위로 메모리에서 값을 가져오기 때문에
만약 2번지 주소에 저장되어있다면
0번부터 불러서 0,1,2,3 을 부른 후, 다시 4번부터 또 불러 4,5,6,7 을 가져와서
우리가 저장했던 값을 가져온다. 즉, 메모리 호출을 2번 하게 되는 것이다. 메모리에서 값을 가져오고 쓰는 과정은 굉장히 비싼 작업이다. (시간이 오래걸린다.) 그렇기 때문에 메모리에 값을 저장할 때 신경을 써줘야한다. 보통 이 작업을 컴파일러가 알아서 해준다. 하지만 프로그래머가 특별히 어떠한 단위로 저장을 원할 경우 __attribute__의 aligned 속성을 사용해 지정해 줄 수 있다.

*  __attribute__ ((aligned(4))); 를 이용한 해결
위의 경우 2번지에 저장이 되었는데 그 앞에 char 변수 2개가 들어있는 구조체라고 하자.
그렇다면 위의 상황이 나온다. (보통 요즘의 컴파일러들이 알아서 구조체에서 가장 큰 사이즈의 단위로 alignment를 해주지만 안해준다고 치자!)
그렇다면 여기서 우리는 구조체 선언할 때 뒤에 __attribute__ ((aligned(4)));를 붙이므로써 이 문제를 해결할 수 있다.
그렇게 되면 0번지부터 char 변수 2개가 들어가고, 메모리 2,3 주소에 패딩이 들어간다.
패딩이란 메모리 단위를 맞춰주기위한 더미값? 이라고 생각하면된다.
그리고 int값을 쓸 때는 4번지 주소부터 적게 되어 아까 위의 문제가 발생하지 않게 되는 것이다.


지난 포스팅에서 우주선 자체가 깜빡 거리는 것 까지 했다.

이번에는 우주선이 움직이는 것을 표현해 볼 것이다.

오른쪽으로 이동하면서 우주선이 빙글빙글 돌면서 날라간다. 날라가면서 크기도 작아진다. 그리고 화면 밖으로 벗어나게 되면 그 반대에서 다시 나타나서 계속 움직이는 것을 만들 것이다.


(update)


update 함수를 수정해주면 된다.

우주선을 회전하게 하는 것을 각도 설정, 크기 설정에서 크기만 넣어주면 된다. 어떻게 작아지는지 어떻게 이미지 생성 위치가 바뀌는지에 대한 것은 애니메이션 그리는 것에서 우리가 다 만들어 놓은 것이다. 우리는 단지 회전각도와 위치만 설정해주면 된다.



(상수)


위 코드에 대한 상수값들이다.
회전 상수는 180으로 정의 되있어서 초당 180도를 회전한다는 의미이다.
SCALE_RATE 상수는 0.2로 정의 되어있어 초당 100픽셀만큼 움직인다는 의미이다.

SHIP_SCALE 상수는 1.5로 정의되어있는데 우주선이 원래 크기에서 1.5배로 커진다는 의미이다.


(실행 화면)


실행 시 깜빡이는 우주선이 오른쪽으로 한없이 날라다닌다.

이제는 화살표 키 입력을 받아 우주선의 움직임을 통제 해 볼것이다.



(update)


위에서 한것과 같이 단순히 키 입력만 확인하여 그에 따라 위치만 바꾸어 주면된다.

(실행화면)


실행시 키 입력에 따라 우주선이 움직인다. ( 사진으로 해놔서 의미가 있는지 모르겠다... )

그런데 키를 때면 바로 우주선이 멈춘다.
키를 때도 그 방향으로 계속 움직이게 수정해 줄 것이다.
반대 키를 누르면 그 방향에대한 속도가 줄게 할 것이다. 그러기 위해서는 속도라는 값이 들어가야한다.

키 입력에 따라 속도 값을 수정해주고, 그 속도만큼 움직이게 하면 되는 것이다.



(update)


위 처럼 해두니 키를 때었을 때 계속 날라다니는 현상이 있었다.

키를 때었을 때 점차 속도가 줄어들게 하고 싶었다. 그래서 update 아래에 아래 코드를 추가했다.



(추가한 코드)


GRAVITY는 실제 중력값은 아니고 속도가 감소한다는 의미의 상수를 넣었다.



(추가 상수)



(실행 화면)


실행 해보니 우주선이 잘 움직였고, 키를 때었을 때 점차 속도가 감소하였다.

마지막으로
속도가 좀 느리다는 느낌이 있었다.

그래서 스페이스바를 누르면 부스트 모드로 기존 속도의 2배로 날라가게 만들었다.


(추가한 코드)


스페이스바 입력을 받게 하기 위해 상수로 추가했다.



(스페이스바 추가)


그리고 boost 여부에 따라 2배로 움직이게 만들었다.



(boost 모드)


실행시 아주 아주 잘 동작하였다.
키 입력을 받아 우주선이 움직이게 하는 것까지 마무리했다.!!

(* 참조 - 2D 게임 프로그래밍, 찰스 켈리)



저번에 그렸던 이미지에서 간단한 애니메이션을 추가해 볼 것이다.

우주선을 추가할 것인데, 우주선 주변에 불이 깜빡깜빡하는 아주 간단한 애니메이션을 추가해 볼 것이다.
가장 먼저 해야할 일은 우주선을 추가해주어야하는 일이다.

저번에 이미지를 그렸을 때와 같이 우주선 textureManager와 Image 클래스를 만들어준다.

(우주선 추가)


그 후 초기화 함수에도 추가해준다.


(텍스처 초기화)



(이미지 초기화)


이미지 클래스 초기화 후

우주선의 화면 위치를 설정해준다. 여기서는 좌측상단에 나타나도록 설정해두었다.
그 애래의 코드들은 우주선 애니메이션과 각도에 관한 설정들이다.
뒤에서 설명하겠다.!

애니메이션을 동작시키기 위해 애니메이션이 있는 이지미를 계속 업데이트해줘야한다.

이 과정은 게임 클래스 안의 update 함수 안에서 해당 이미지의 업데이트를 추가해주므로써 구현한다.



(이미지 update)


우주선의 업데이트 함수를 호출했다. 여기에 인자는 frameTime을 넣었다.
예전에 게임엔진 틀을 만들 때 frameTime을 계산해주기 위해 내부 타이머를 이용해 시간을 계산해주는 작업을 한 것이 기억날 것이다. 이런 곳에 사용하기 위해 계산을 해둔 것이다.

이미지클래스의 update를 확인해보자


(update 함수)


넘어온 frameTime은 한 루프가 돌면서 지난 시간이다. (이전 게임엔진 틀을 만들 때 만들었었다.)
먼저 체크하는 것은 애니메이션 동작이 있는 스프라이트인지 체크한다.
애니메이션 동작이 있는 것이라면 먼저 경과 시간을 계산해준다.
우리가 원하는 frameDelay마다 동작하게 하기 위해서이다. 그래서 그 다음으로 frameDelay보다 큰지 확인하고 크면 update를 하고 크지 않으면 다음 루프에서 update를 하게하기 위해서이다.
그 후 애니메이션 타이머에서 frameDelay를 빼준다. (다음 번 update에서 사용하기 위해서)
그리고 현재 프레임수를 1증가시킨다.
그리고 동작하는 것이 setRect() 함수이다.

이제 이 setRect() 함수를 보면 애니메이션이 이해가 갈 것이다.



(setRect() 함수)


이 함수에서는 이미지에서 특정 영역을 설정하는 함수인데, 현재frame을 기준으로 바뀌는 것을 볼 수 있다. 우리가 올린 이미지는 아래와 같다.


(우주선 이미지)


위의 이미지에서 하나씩 가져다 쓰는데 4개를 번갈아가면서 화면에 출력해주는 것이다.
그렇기에 우주선 주위에 불빛이 빤짝거리게 되는 원리이다.

즉, 움직이게 하고 싶은 이미지 마다 위와 같은 과정으로 프레임을 설정해 주는 것이다.

맨 처음에 봤던 우주선 애니메이션활성화 설정을 다시 볼 것이다.


(애니메이션 설정)


setFrames는 처음 프레임과, 마지막 프레임을 설정하는 것이다. 변수의 값은 아래 사진과 같다.


(상수)


프레임은 0에서 3까지 총 4개의 프레임이고 위의 사진에서 보았듯이 4개의 우주선 모양이 있는 것을 확인 할 수 있다. 그리고 setCurrentFrame 함수로 현재 프레임을 우주선의 시작 프레임으로 설정해주는 것이다. (초기화 함수이기 때문에)



(setCurrentFrame 함수)


currentFrame에 넘어온 인자를 넘겨준다. 그리고 영역을 설정한다.(현재 프레임에 맞는 그림)

자! 이제 실행해 볼 것이다!

그 전에! 추가해줘야할 것이 있다. render() 함수에 우주선 이미지 그리는 것을 추가해줘야한다.!


(우주선 그리기)


이제 빌드 후 실행??
나는 여기서 빌드 후 실행하고 잘 되는 줄 알았다.
그리고 곧 문제점을 깨달았다.
전체화면으로 바꾸려고 했을 때 되지 않았다.!! 화면이 까매지기만 했다. 디바이스 로스트 상황이었다.

코드를 다시 보다가 이해했다. 디바이스 로스트 때 돌아가는 것 중 우리가 우주선 이미지를 추가하면서 우주선 textureManager를 추가해줬는데 이것도 핸들해줘야하기 때문이다.


(로스트 처리)


이렇게 로스트 처리하게 되면 창전환도 괜찮았다.



(실행 화면)



우주선 주변의 불빛이 깜빡거리는 간단한 애니메이션을 추가해보았다.

(* 참조 - 2D 게임 프로그래밍, 찰스 켈리)


지금 까지만든 설문지페이지에서 오늘은 form을 작성해 볼 것이다.

 HTML에서 form은 데이터를 전달 하는 기능을 한다.
우리는 설문지에서 설문 선택지를 선택하고 버튼을 클릭하면 선택한 선택지의 정보를 담아서 웹 페이지에 전달할 것이다.

이 내용은 DB에 저장될 것이므로, POST 방식으로 보내게 할 것이다.


(detail.html)


detail.html 템플릿은 question 객체를 받는다.
받은 question 객체에서 질문 내용인 question_text를 h1 태그로 찍어준다.

그리고 에러메시지가 있으면 해당 에러 내용을 출력해준다.

그 다음 부터는 form 태그이다.
그런데 중간에 csrf 토큰이 보일 것이다. 우리는 form 을 사용하여 데이터를 전달할 것이기 때문에 CSRF 공격에 대해 생각하고 있어야한다. 장고에서 대신 CSRF 공격에 대해 예방하는 기능이 있는데
{% csrf_token %} 만 작성하면 된다. (나중에도 form 문에는 이 문구를 적어넣어야한다. )
해당 question객체에서 선택지들을 전부 가져온 후 for 문을 돌면서 radio 버튼과 선택지 내용을 출력한다.

웹페이지를 실행시켜서 직접 확인해보자!



(form 페이지)


질문 내용이 위에 찍혀있고 선택지들이 버튼과 함께 나열되어있는 것을 볼 수 있다.

위에 form 코드를 다시 보면 url vote로 이동하라고 action에 나와있다.

url vote는 우리가 작성한 polls/urls.py를 확인하면 알 수 있다.


(vote)


vote이름을 가진 주소패턴에 해당 question.id가 조합이 되면 어떤 주소가 된다.
보면 views.vote를 실행하게 되어있다.

그렇다면 views.vote를 작성해주자!


(views.vote)


우리는 question_id를 주소에서 전달받게 된다. 전달 받은 question_id를 pk로 사용하여 해당 질문 객체를 가져온다.
그 후 선택된 선택지를 가져오는데 request객체 안에 POST에 담겨있다. 우리가 POST로 전달했기 때문이다. POST 속에 choice=### 이런식으로 담겨온다. choice변수에 담긴 값을 pk로 선택된 질문을 가져온다. 그 후 선택된 질문객체에서 투표수를 1 증가시키고 저장한다.
그리고 Redirect한다. reverse를 사용해서 url 주소를 만든다.
polls 이름 영역의 urls.py에 정의 되어있는 results 이름의 주소 패턴을 가져오고 인자로 question.id를 조합하여 특정 url을 만드는 것이다. 그 주소로 리다이렉트한다.

그렇다면 처리된 결과를 확인하는 results의 화면도 만들어보자.


(views.results)


위에서 리다이렉트된 주소는 views.results를 호출한다. polls/urls.py를 확인하면 알 수 있다.
이 뷰에서는 질문 객체를 가져오고 이 질문 객체를 가지고 polls/results.html 템플릿을 보여준다.

보여주는 results.html 템플릿은 아래와 같다.


(results.html)


질문 객체에서 각각의 선택 내용과 투표수를 출력하는 템플릿이다.

실행해서 확인해보자.



(실행 확인)


확인해보니 잘 된다!

질문 객체를 가져오고, 질문 객체를 가지고 템플릿으로 전달하고 등등
어떻게 보면 이것 또한 루틴한 작업이다.
그래서 이번에는 제네릭 뷰를 이용해서 위의 페이지를 똑같이 만들어 볼 것이다.

제네릭 뷰에는 리스트뷰와 디테일뷰가 있다.
리스트뷰는 말 그대로 리스트들을 보여주는데 사용하고
디테일뷰는 우리가 detail view를 만들었던거와 같다.

먼저 제네릭뷰를 사용하기 전에 polls/urls.py를 수정해줘야한다.


(polls/urls.py)


제네릭뷰에서는 pk로 전달 받기 때문에 pk로 바꾸어 주었다.
그리고 호출하는 것이 각각의 뷰에서 as_view() 함수를 호출한다.
우리는 views에 위에서 사용한 클래스를 만들어줘야한다.

이어서 view를 수정해 줄 것이다. 제네릭 뷰를 이용해 수정한 코드는 아래와 같다.


(제네릭 뷰)


각각의 클래스를 만들었다.
template_name은 우리가 사용할 템플릿 위치를 알려준다.
그리고 model은 어떤 모델이 적용될 것인지 지정해주는 것이다.
DetailView의 경우 question 변수가 자동으로 제공된다. 그러나 ListView의 경우 자동 생성된 컨텍스트변수는 question_list이기 때문에 우리가 템플릿에서 사용했던 변수 이름으로 맞춰주기 위해 context_object_name의 변수로 지정해주었다.

ListView에서 get_queryset을 가지고 쿼리를 날리는데 우리는 최근 5개 글을 볼것이므로 해당 함수를 덮어쓴 것이다.

실행해서 웹페이지에서 똑같은 동작인지 확인해보자



(동작 확인)


같은 동작을 하는 것을 확인 할 수 있다.

(* 참조 - https://docs.djangoproject.com/ko/2.0/intro/tutorial04/)

'Web > Django Project' 카테고리의 다른 글

Simple SignUp  (1) 2018.07.25
Simple Login/Logout  (0) 2018.07.24
Django Tutorial - Views ( Template와 연결 )  (0) 2018.02.01
Django Tutorial - 관리자 생성 및 페이지보기  (0) 2018.01.31
Django Tutorial - Model  (0) 2018.01.31
게임 프로그램에 이미지를 그리는 것을 해볼 것이다.

우리가 사용할 것은 스프라이트이다.
스프라이트는 2D 이미지를 그리기 위해 설계된 DirectX 그래픽스 API의 일부이다.
(꼭 2D 이미지만을 위한 것은 아니지만 거의 주로 2D이미지에 사용된다.)

그렇다면 스프라이트를 이용하기 위해 스프라이트 포인터를 자주 사용하므로 간단하게 재정의한다.


(스프라이트 재정의)


앞으로 LP_SPRITE로 사용할 것이다.

그리고 스프라이트 포인터를 담고 있을 변수도 추가해준다.

스프라이트는 그래픽을 그리는 일을 하므로 그래픽 클래스에 변수를 만들것이다.


(스프라이트 포인터 변수)


그렇다면 이제 할 일은 스프라이트를 생성하고 그 스프라이트 주소를 우리가 만든 스프라이트 포인터 변수에 담아야 한다. (만들어야 사용할 수 있으므로 당연한 사고의 흐름이다!)



(스프라이트 생성)


스프라이트 생성함수는 위와 같다.
D3DXCreateSprite를 사용하고 첫번째 인자에 디바이스 인터페이스를 넘겨주고, 두 번째 인자는 생성될 스프라이트 객체의 주소를 담을 포인터 변수이다.

게임 엔진을 통해 수행되는 모든 스프라이트 그리기 작업은 하나의 DirectX 스프라이트로 처리된다. 앞으로 지금 만든 스프라이트를 이용해 그리기 작업을 수행할 것이다.

그 다음으로 이미지 파일로 부터 이미지를 가져와 텍스처를 만들것이다.
텍스처?
텍스처는 그래픽스에서 사용하는 프리미티브 타입에 적용할 수 있는 그림이다. 이 경우 텍스처는 스프라이트이다. ( 텍스처에 관해 궁금하신 분들은 구굴링! )
텍스처는 여기서 쉽게 말해 그릴 그림이라고 생각하고 넘어가도 된다. ( 2D 이미지를 그리는 경우 )

쉽게 정리해서
파일로부터 이미지를 가져와 그릴 수 있는 텍스쳐로 만든 후
스프라이트를 이용해 그 텍스쳐를 화면에 그린다!

그렇다면 파일로부터 이미지를 가져와 텍스처를 만드는 것을 계속해보자!



(텍스처 가져오기)


D3DXCreateTextureFromFileEx 함수를 이용해 파일로 부터 이미지를 가져오고 그 이미지로 텍스처를 만드는 작업을 한다. 맨 마지막 인자에 포인터 변수가 들어가는데 그 변수에 만들어진 텍스처가 저장된다.

중간 인자 중 텍스처의 폭과 높이가 있는데 이 정보를 파일로 부터 가져올 수 있다.
D3DXGetImageInfoFromFile을 사용하면 되는데 이 것을 포함해 텍스처를 로드하는 함수인

loadTexture의 완성 코드는 아래와 같다.


(loadTexture)


loadTexture함수는
파일로 부터 폭과 높이를 가져오고
그 후 텍스처를 만드는 함수이다.

이제 스프라이트를 그리는 작업을 살펴볼 것이다.
스프라이트는 신 사이에서 그려야한다.
신 시작 - (그리기) - 신 끝
위 처럼 신의 시작과 끝 사이에서 그려져야한다.

위의 원칙을 적용해 renderGame 함수를 적어보면 아래와 같다.


(renderGame)


renderGame함수는 게임 아이템을 그리는 함수이다.
렌더링 시작할 때 그래픽 함수에서 신을 시작하는 함수를 호출하고
그 후에 render() 를 호출한다. (여기서 그리기 작업이 들어가는 것이다. )
그리고 다 그렸으면 신을 종료하는 함수를 호출하여 그리는 작업을 마무리한다.
(지금까지 그리는 것들은 백버퍼에 그려지고 있는 것이다.)

그 후 백 버퍼를 화면에 표시해준다.

신을 시작하고 종료하는 함수는 아래와 같다.



(신 시작과 끝함수)


beginScene 함수는 신을 시작하는 함수이다.
신을 시작하기 전에 백버퍼를 지워 준 후 신을 시작한다.
endScene 함수는 신을 종료하는 함수이다.
내부적으로 디바이스 인터페이스에서 BeginScene과 EndScene을 호출한다.

신 시작 함수 호출과 스프라이트 그리기 함수 사이에서 한가지 작업이 더 들어가야한다.
그리기 전에 그릴 텍스처에 대한 설정이다. 크기 조정, 회전 및 위치를 지정하는 작업이다.

이 작업은 행렬 수학을 이용해 계산이 되는데 우리는 직접 계산해서 하는 것이 아니라 DirectX의 함수를 이용할 것이다.


(행렬 적용 및 그리기)


D3DXMatrixTransformation2D 함수를 사용하면 크기 조정, 회전 및 위치를 지정할 수 있는 변환 행렬을 구할 수 있다. 첫번째 인자로 만들어진 행렬이 저장된다.

만들어진 행렬을 SetTransform 함수에 넣어 호출해주면 해당 행렬을 스프라이트에 적용하게 되는 것이다.

그 후 Draw 함수를 이용해 그리기 작업이 들어가는 것이다.

이렇게 그리는 텍스처에 대한 속성값들이 있다. 이것들을 위한 구조체를 만들어서 정리하면 편하다.


(스프라이트 속성)


스프라이트 속성들을 모아서 구조체로 만들었다.

아래는 스프라이트 속성들을 설정하는 코드이다.


(스프라이트 속성 설정)


위 코드는 drawSprite 함수의 윗부분에 해당하고 이 코드들이 실행이 된 후 속성이 다 설정이 되면 위에서 봤던 행렬을 만들어준 후, 적용하고 그리는 단계로 drawSprite 함수가 이루어져 있다.

정리하면!
1. 그래픽 클래스에서 신을 시작한다.
2. 스프라이트의 Begin 함수를 호출
3. 스프라이트 속성 설정
4. 스프라이트 행렬 만들기
5. 스프라이트 행렬 적용
6. 스프라이트 그리기
7. 스프라이트의 End 함수 호출
8. 그래픽 클래스에서 신을 종료한다.


아래는 색상을 앞으로 이용하기 편하게 만들어 놓은 이름 공간이다.


(색상 namespace)


예를 들어 오렌지색을 사용하고 싶다면 코드에
graphicsNS::ORANGE라고만 입력하면 된다.

그래픽 클래스에 추가한 loadTexture 함수는 텍스처 데이터를 D3DPOOL_DEFAULT 메모리로 불러온다. 일반적으로 이 곳은 비디오 메모리인데, 다른 프로그램이 그래픽 디바이스를 사용하고 있다면 사용할 수 없거나, 로스트 상태가 된다. 그렇기에 로스트 상태가 되면 디바이스를 되찾는 과정이 필요하다.

게임에서 텍스처를 사용할 때 각 텍스처를 불러오고 관리하기 위해 TextureManager 객체를 만들 것이다.


(초기화함수)


초기화 함수에는 loadTexture를 이용해 해당 이미지를 가져와 텍스처를 생성하는 작업을 한다.

로스트 상태를 관리하기 위한 함수는 아래와 같다.


(로스트 관리)


이전 다른 디바이스와 마찬가지로 로스트 상태라면 해당 리소스를 해제해주어야하고 다시 얻어와야한다.

그 외 TextureManager 객체에 대한 데이터를 가져올 수 있는 함수들도 만들어준다.


(TextureManger 함수들)


이 함수들을 이용해 해당 텍스처에 대한 정보를 가져올 수 있다.
마지막으로 스프라이트를 그리고 관리하는데 필요한 모든 코드들을 Image 클래스로 통합할 것이다.

Image 클래스를 이용해 그리기는 작업을 할 것이다. (지금까지 만든것들을 Image 클래스에 통합하는 과정이다.)


(이미지 클래스 초기화함수)


초기화 함수에서는 필요한 객체를 가져오고, 해당 이미지의 텍스처 정보를 가져오는 과정이다.

이미지 클래스의 draw 함수이다.


(그리기 함수)


이 함수에서 그래픽의 drawSprite 함수를 호출한다. 그러면 우리가 지금까지 확인한 일련의 과정들이 실행되는 것이다.

draw 함수를 두개 만들었다. 하나는 위와 같고 다른 하나는 특정 sprite를 지정하여 그리는 함수로 아래와 같다.


(특정 스프라이트를 지정하여 그리는 함수)


그 외에 이미지 클래스의 데이터를 가져오고 설정하는 get과 set 함수들을 추가해준다.

그러면 지금까지 만든 내용들로 게임의 이미지를 그려볼 것이다.
성운 이미지를 배경으로 쓰고 행성 이미지를 그 위에 그리는 것이다.

Spacewar.h 함수에 게임 아이템 2개에 대한 텍스처와 이미지를 만들어준다.


(텍스처와 이미지)


그리고 초기화 과정에 텍스처 초기화과정과 이미지 초기화 과정을 넣어준다.


(게임 초기화 함수)


처음에 텍스처 초기화 과정이 이루어지면 그 텍스처매니저를 이미지 초기화 함수에 사용한다.
그리고 행성을 화면 중앙에 배치하는 코드를 넣었다.

이렇게 되면 그리는 작업의 준비는 끝난 것이다.

그리는 함수인 render() 함수에 해당 이미지를 그리는 함수를 호출하면된다.


(그리기)


위 render 함수 이전에 실행되는 것이 그래픽 클래스의 신 시작이고 render()함수 가 종료되고 실행되는 것이 크래픽 클래스의 신 종료 함수인 것을 기억해야한다.

그리는 것은 이미지 클래스의 draw(우리가 만든것) 을 이용한다.


(로스트 처리)


로스트 처리에 관한 부분도 넣어준다. 각각의 아이템마다 처리해준다.

그리고 빌드 후 실행해보자!


(실행 화면)



프로그램에 우리가 원하는 그림이 그려지는 걸 확인 할 수 있다.

(* 참조 - 2D 게임 프로그래밍, 찰스 켈리)

이제는 views.py를 작성해 볼 것이다.

views 와 url을 연결하는 것은 지난 포스팅에서 다루었다.
이제 그 views.py를 작성 할 것이다.

개발환경이 바뀌었다.!!!!
우분투 너무 느렸는데, CentOS로 옮겼다. 아주 쾌적하고 좋았다. :)

우리는 4개의 view를 만들어야했다.
1. index : 설문지 리스트를 보여주는 view
2. detail : 특정 설문지에 들어가서 선택지들을 보여주는 view
3. vote : 선택지를 선택한 것에 대한 처리를 담당하는 view

4. results : 설문 결과를 보여주는 view


(views.py)


위와 같이 간단히 작성할 것이다.
지금 단계로는 우리가 계획한 view를 바로 설계할 수 없다.
위에 작성한 코드들은 해당 페이지와 연결을 하는 것을 해보기 위해서이다.
보면 view에 인자들을 살펴봐야한다.

첫번째 인자는 request 로 요청에 대한 객체이다.

두번째 인자는 넘겨 받는 인자인데, 우리가 urls.py를 작성할 때 url 주소에서 파싱한 내용들이다.


(polls/urls.py)


polls/urls.py 에 주소 패턴에 넘겨주는 인자가 있다.

이대로 실행해보자.
polls/3  의 주소를 요청하게 되면

뒤에 3이 question_id 라는 변수에 들어가서 넘겨가는 것이다.



(실행 화면)


3이라는 숫자가 question_id로 넘어가서 화면에 저렇게 표시 되는 것을 확인 할 수 있다.


이제 실제로 view에서 무언가를 처리하는 것을 넣을 것이다.
위에서는 단순하게 인자를 넘겨받는 것 까지만이었다.

최근 설문지 5개를 가져와서 출력하는 것을 해보자!



(views.index)


latest_question_list에 최신의 설문지 5개를 가져온다. 반복자형식이다.
그리고 해당 설문지의 설문내용을 하나씩 붙여서 output에 넣고
그 output을 화면에 출력해주는 코드이다.

확인!


(확인)


확인해보니 설문내용이 출력되는 것을 확인 할 수 있었다.

장고는 모델과 뷰, 템플릿이 따로 관리되는 MTV 구조를 가지고 있다.
그에 따라 우리도 템플릿을 만들어서 여기서 보여주는 것을 해볼 것이다.

템플릿은 settings.py 파일에서 INSTALLED_APP 디렉토리에서 하위 templates 디렉토리를 탐색한다.
고로 우리는 polls/templates 를 만들어야한다.

여기서 다시 polls 디렉토리를 만든다.
정리하면,
polls/templates/polls
이렇다.

이유는 장고가 템플릿을 찾을 때 이름이 일치하는 첫번째 템플릿을 가져온다.
그렇다면 동일한 템플릿 이름이 다른 APP에도 있다면? 어떻게 구분할 수 있을까?
그렇기 때문에 templates디렉토리 아래에 해당 어플 이름으로 디렉토리를 하나 더 만들어 주어서
템플릿에 이름을 구분시켜주는 것이다.

polls/templates/polls/index.html 을 만들자!



(polls/templates/polls/index.html)


위 템플릿은 latest_question_list 를 넘겨받는다.

코드 설명
latest_question_list에서 하나씩 가져와서 question에 넣으면서 반복한다.
question의 id 값을 이용해 링크 걸린 문구를 출력한다.

만약 가져올 설문이 하나도 없다면
설문이 없다고 출력한다.

이렇게 템플릿을 만들었으니

우리는 view에서 이 템플릿과 연결해야한다.



(polls/views.index)


template에 템플릿을 가져오고
context 에 latest_question_list 변수를 지정한 사전형 자료를 담아둔다.

그리고 template.render를 해주면 된다.



(결과)


이렇게 실행하게 되면
웹 페이지에서 링크로 출력되는 것을 확인 할 수 있다.

여기서 의문이 든다.
템플릿 로드하고, 변수 저장하고 템플릿 렌더해서 HttpResponse로 넘겨준다.
너무 루틴하지 않은가?
우리의 파이썬이 이걸 가만 놔둘리 없다.

단축기로 render라고 만들어두었다.
이 모든 과정을 한방에 해준다.

단축기를 사용해보자.



(단축기 사용)


render를 사용하면 위의 과정을 한번에 해준다.
단순히 템플릿을 지정해주고, 넘겨줄 변수를 사전형으로 만들어서 넘겨주면된다.

views에서 처리해야할 일이 또 뭐가 있을까?
바로,
에러처리다.

웹 페이지에서 대표적인 에러는 바로 404 에러이다.
해당 웹 페이지가 없는데 요청하는 것이다.
예를 들어 /polls/24  -> 만약 id가 24가 없다면 해당 페이지는 준비 되지 않은것이다.

404 에러 처리를 해보자!



(404 에러처리)


파이썬의 try except 구문을 사용한다.
해당 쿼리를 날려서 설문지를 가져오는데 만약 그게 없다면
404 에러를 반환한다.

실행하기 위해 detail.html을 만들어야하는데 형식상 만드는 것이므로 지금은 아주 간단히 작성한다.



(polls/templates/polls/detail.html)


없는 인덱스로 실행해보자.


(404 에러)


우리가 적은 Question does not exist 보이는가?
우리가 404 에러 처리를 하였다.

그렇다면 여기서도 또 의문이 든다.
try except 구문
왠지 많이 자주 쓰일 것이다.

그렇다!
이것 또한 단축기로 만들었다.!!!!
get_object_or_404 를 이용하면 해당 객체를 가져오는데 만약 없다면 404 에러를 내는 

우리가 위에서 했던 것을 한방에 해주는 함수다.



(단축기 사용)


실행해본다.



(실행 결과)


똑같이 404 에러가 떴다.
다만 에러 문구는 다르다!

이제 detail.html 템플릿에서

선택지를 보여주는 작업을 해볼 것이다.



(polls/templates/polls/detail.html)


해당 템플릿에서
question 객체를 받았다.
설문지에서 선택지를 전부 가져와서 하나씩 choice에 넣고
choice의 선택지를 출력해주는 것을 반복하는 코드이다.

실행해보면

(실행)


선택지들이 나열되어 나온다.


index 템플릿에서 detail 템플릿을 호출하였다.



(index 템플릿)


index 템플릿에서
/polls/ 
이렇게 url주소가 하드 코딩되어있다.

그렇다면 만약 detail 템플릿의 url이 바뀌면
이렇게 하드코딩된 주소를 찾아서 일일히 다 바꿔야할까?
그렇기 때문에 url 주소를 간편히 쓸수 있는 것이 있다.


(하드코딩 제거)


위처럼 {% url '이름' %} 이렇게 쓰면 된다.

그렇다면 저 url은 뭘까?


(urls.py)


우리가 urls.py에서 마지막 인자로 name 인자를 둔 것이 기억날 것이다.
이것이 우리가 url패턴을 각각 이름으로 정한것이다.

그렇기에 detail url을 쓰고 싶다면
{% url ' detail' %} 이렇게 쓰기만 하면 되는 것이다.

그렇다면 이 이름이 겹치는 것을 생각해봐야한다.
우리는 APP을 여러개 만들 건데 그 url마다 이름을 다 다르게 해야하는 건 너무 힘든 일이다.
다른 APP에 detail이라는 url 이름이 있을 수 있기 때문이다.

이걸 방지하기 위해 app_name을 지정해 줄 수 있다.



(APP 이름 지정)


위 처럼 app_name을 지정해주면 polls에서 사용하는 detail 이라고 알 수 있다.

그렇다면 당연히 아까 적은 url 표기도 바꾸어 주어야한다.


(url 표기 변경)


polls:detail
이렇게 바꾸면 된다.

(* 참조 - https://docs.djangoproject.com/ko/2.0/intro/tutorial03/)



이번에는 관리자를 생성한 후 관리자 페이지를 확인해 볼 것이다.

먼저!

관리자 계정을 생성한다!


(관리자 계정 생성)


명령어는 위와같고, 각자 계정과 비밀번호를 설정해주면 된다.

관리자 페이지란 장고에서 제공하는 기본적인 관리페이지이다.
우리는 이 페이지에서 우리가 만든 프로젝트를 관리할 수 있고 db도 쉽게 관리할 수 있다.

페이지는 /admin 으로 접속하면 된다.



(관리자 페이지)


아까 생성한 관리자 계정으로 접속한다.



(관리자 로그인)


로그인을 하면 위와 같은 화면이 나온다.

그런데?
우리가 아까 만든 model 정보는 없다..!
그렇다.
연결해야한다.

polls/admin.py로 가주자.



(polls/admin.py)


해당 파일을 위와 같이 작성한다.
register 함수로 Question 모델을 등록하는 것이다.

다시 접속해보면?


(관리자 페이지)


관리자 페이지에서 POLLS가 생성되고 아래 Question 항목이 생긴것을 볼 수 있다.

클릭해보면 아래와 같다.


(DB 수정)


해당 DB의 내용들을 볼 수 있고, 수정할 수 있고 추가할 수도 있다.
DB를 관리자 페이지에 연동시켜주면 나중에 DB관리도 장고에서 제공해주는 서비스를 이용하여 아주 편리하게 관리할 수 있다.

(* 참조 - https://docs.djangoproject.com/ko/2.0/intro/tutorial02/)

+ Recent posts