[Flask로 블로그 만들기] 5. 대댓글과 댓글 관리 구현
지난번 만든 댓글 기능을 강화했습니다. 대댓글(답글) 기능은 최근의 사이트는 모두 가지고 있고, 댓글 관리 기능은 필수로 있어야 하는 기능입니다. 댓글이라는 점을 넘어서, 인터넷 상에서 자신의 흔적을 지우는 것은 기본적인 인터넷 이용자의 권리입니다. (물론, 실제로는 당장 ISP부터 산더미 같은 로그를 쌓지만요.)
대댓글 작성 기능 구현
대댓글 기능은 간단합니다. 이미 DB에는 '부모 댓글의 ID'를 저장하는 필드가 있습니다. 일반적인 상태에서는 이 부분은 항상 0
이 적혀있지만, 부모 댓글이 있는 경우(=대댓글을 다는 경우)엔 부모 댓글의 ID가 저장됩니다. 제 경우엔 ID는 순번으로 정했기 때문에, 만약 세 번째로 작성된 댓글의 자식이라면 그 부모 댓글의 ID
열에는 3
이 기록될 것입니다. 당연히 이 '순번'은 고유하게 작용해야하기 때문에 댓글이 삭제되면 그 순번은 결번되어야 합니다. 순번을 Primary key로 지정하면 겹치지 않는 고유의 인덱스로 작용합니다.
아무튼, 중요한 점은 [대댓글 작성] 버튼을 누르면 댓글 입력창의 폼 요소에 부모 댓글의 ID를 추가합니다. 이렇게 하면 Flask에서 부모 댓글의 ID를 DB에 함께 저장할 수 있습니다. 버튼은 링크가 아니기에, <a>
태그가 아니라 <button>
태그를 사용해야 옳습니다. 이 버튼을 누르면 onclick
이벤트에 의해 댓글 입력창의 폼 요소에 값을 추가하는 것입니다.
이로써 DB에 부모 댓글의 ID만 기록된다면, 지난번 구현했던대로 "댓글과 대댓글을 구분하여 목록을 구현" 할 수 있는 것입니다.
댓글 관리 구현
댓글 관리 기능도 마찬가지입니다. 이번엔 팝업을 이용하겠습니다. 자바 스크립트를 이용하여 댓글을 관리할 수 있는 팝업을 생성합니다.
try { openWindow.close(); } catch (e) { }
openWindow = window.open("/modify-comment?num=" + _num, "댓글 관리", "width = 450, height = 550, status=no, menubar = no, toolbar = no");
openWindow.focus();
실제 적용된 팝업을 여는 코드입니다. GET 방식으로 댓글 관리 페이지를 열게 됩니다. 첫 줄의 try ~ catch는 이미 창이 열려있을 때 두 개 이상의 창이 열리지 않게 하기 위함입니다. try ~ catch를 이렇게 느슨하게 사용하는 것에 약간 꺼려지지만, 간단한 방법을 놔두고 수 줄의 코드를 작성하는 것은 더욱 피하고 싶습니다.
그 외에 요즘은 무의미한 옵션이 window.open()
함수에 인수로 전달되었긴 합니다. 최근 인터넷 브라우저는 팝업 사이트를 이용한 피싱을 방지하기 위해 일부 창 요소를 '숨기는' 옵션이 적용되지 않습니다.
아무튼 이렇게 GET 방식으로 주소를 요청하면, Flask에선 먼저 인증 페이지를 보여줍니다. 인증 페이지는 사용자의 비밀번호를 묻습니다. 비밀번호는 똑같은 솔트를 적용하여 똑같은 해시함수로 해시하여 DB에 저장된 암호(해시화됨)와 비교합니다. 당연한 상식이지만 암호 원문은 저장해서도, 비교해서도 안됩니다. 암호가 일치하면 이제 댓글 관리 페이지를 엽니다. 댓글의 본문과, 수정/삭제 버튼이 있죠.
수정 버튼을 누르면 SQL의 UPDATE문을 이용하여 댓글을 수정하며, 삭제 버튼을 누르면 DELETE문을 이용하여 삭제합니다. 제가 알기론 많은 사이트들이 단순히 DB에 삭제 플래그만 표시하여 실제론 서버에서 삭제되지 않지만 마치 댓글이 삭제된 듯 외부로 보여주지 않는 방식을 갖는 것으로 알고 있습니다. 그러나 자신이 적은 '인터넷 흔적'이 남의 서버에 남는다는 것은 꽤 께름칙한 일입니다.
중요한 것은, 수정과 삭제 버튼을 누를 때 반드시 서버에서 또 검증을 해야한다는 점입니다. 이를 위해서 암호를 처음 입력할 때 일회성 토큰을 사용자에게 부여하고, 해당 토큰이 일치할 때만 쿼리할 수 있게 했습니다. 토큰은 서버만 알고있기 때문에, 사용자가 이를 변형하거나 권한이 없는 다른 글을 수정하려고 하면 서버가 이를 거부합니다.
여담이지만, 삭제 버튼은 원래 회색이었습니다. 그러나 자신의 댓글을 삭제하는 버튼을 회색으로 했었다간 자칫 '취소' 버튼이라고 생각될 수 있었습니다. 큰 고민 없이 만들다가 테스트 중에 그 사실을 알게 되었습니다. '삭제'는 관례적으로 빨간색이었고, '취소'는 관례적으로 회색이었으니까요. 지금은 위 사진처럼 빨간색이 되었습니다.
자바스크립트 미사용 대응
자바스크립트를 사용하지 않는 사람을 위해서 다른 방법을 하나 더 준비했습니다. 이전 포스트에서 언급한대로 자바스크립트를 사용하지 않아도 가능하면 주기능을 모두 이용할 수 있어야합니다. 이는 간단합니다. 기존에 버튼으로 구현했던 동작과 별개로, 댓글을 관리하기 위한 별도의 페이지를 여는 <a>
태그를 배치하면 됩니다. 그리고 <noscript>
태그를 이용해 기존의 버튼을 숨겨주는 스타일을 적용하면 되죠.
<noscript>
태그로 감싼 부분은 자바스크립트를 사용하지 않을 때만 수행됩니다. HTML5부터 <head>
태그 안에도 넣을 수 있습니다. 이 안에서 스크립트를 사용하는 기존 버튼의 스타일을 display: none;
으로 지정해주기만 하면 되죠.
후기
이제 이 시리즈를 끝마치려고 합니다. 아직 적지 못한 부분이 많지만, 지엽적인 부분을 한없이 적어나갈 순 없습니다. 저는 전문 개발자도 아니고 아는 것도 적습니다. 그래서 이런 과정을 통해 새 지식을 알아가는 것이 즐겁습니다. 이번 개발은 특히나 그랬습니다. 그야, Flask로 웹개발을 한 것이 아예 처음은 아니었지만 본격적으로 해본 것은 처음이었거든요.
프론트엔드는 제법 했었습니다. 하다못해 티스토리 블로그의 스킨을 만들 때도 프론트엔드 지식은 필요합니다. 필요한 사이트만 만드느라 되게 못했지만, 모로 가도 서울로 가면 된다고 어찌어찌 원하던 결과물을 얻곤 했습니다. 그러나 이번엔 다릅니다. DB에 직접 값을 저장해야했고, 이를 위해선 백엔드 작업이 필요했습니다. 기존에 이를 PHP로 한 적이 있었지만 제가 미숙한 탓에 코드를 어찌 손 쓸 방도가 없이 꼬여버린 적이 있었습니다. 이번에 Flask로 작업할 땐 이런 상황을 방지하기 위해 최대한 구조적인 코드를 이용했습니다.
Flask는 정말 강력합니다. 마이크로 프레임워크이기 때문에 필요한 모듈은 직접 불러올 수 있습니다. Python 모듈을 공유하기 때문에 그 양도 많고 질도 높습니다. 몇 줄 안되는 짧은 코드로 원하는 코드를 구현할 수 있었습니다. Flask는 산업용 서비스를 개발하기에도 모자람이 없는 훌륭한 프레임워크입니다. 실제로 많은 웹 애플리케이션이 Flask로 개발되고 있습니다.
앞으로도 이 사이트는 계속 개선해나갈 것입니다. 그러나 그 변경 사항을 매번 이 시리즈에 덧붙여서 추가하진 않을 것입니다. 조용히 발전하는 이 사이트는 제 발전을 보여주는 증거가 될 것입니다.
시리즈: Flask로 완전 처음부터 블로그 만들기
5. 대댓글과 댓글 관리 구현 (현재)