Django 템플릿에서 VariableDoesNotExist 예외 오류 대응하기

한 줄 요약 : Django 템플릿 엔진은 템플릿 필터에 대해서 항상 조용한 실패 처리(silent failure)를 하진 않는다.


Django Template은 없는 템플릿 변수나 템플릿 변수의 속성, 키, 색인이 없어도 오류 상황을 일으키지 않고 조용히 오류 상황을 잠재운다. 일명 Silent failure 동작이다.

{{ lorem.ipsum.hello.world }}

lorem이라는 템플릿 변수가 없든 lorem 템플릿 변수는 있는데 이 객체에 ipsum이라는 키나 속성이 없다고 가정하자. 최종 템플릿 맥락이 출력(치환)이면 Django는 변수나 키, 속성이 없다는 오류 상황을 일으키지 않으며, 저 템플릿 변수 위치엔 아무것도 출력되지 않는다. 템플릿 변수를 출력(render)하거나 템플릿 태그에서 사용할 때는 이처럼 Silent failure로 동작한다.

하지만 템플릿 필터를 거치는 경우엔 VariableDoesNotExist 예외(exception)가 발생한다. 예외 이름에서 드러나듯이 템플릿 변수가 없다는 뜻이다.

예를 들어, 존재하지 않는 템플릿 변수인 not_exist_vardivisibleby 템플릿 필터에 사용하면 예외 오류가 발생한다.

{{ '1234'|divisibleby:not_exist_var }}

이에 대해 Django 공식 문서에서는 다음과 같이 설명한다.

Thus, filter functions should avoid raising exceptions if there is a reasonable fallback value to return. In case of input that represents a clear bug in a template, raising an exception may still be better than silent failure which hides the bug. ( 출처 : custom-template-tags - writing-custom-template-filter )

간단히 말해서 템플릿 필터 함수에서는 버그를 숨기는 Silent failure 보다는 예외를 일으키는 게 낫다고 한다. 실제로 Django 내장 템플릿 필터를 보면 대체물을 대신 반환해도 될 만한 경우엔 Exception 처리를 잡아내서 오류 상황을 피하지만, 그 외의 경우엔 Exception이 발생하게 냅둔다. 문제는 그 정책이 예상을 벗어나는 경우에 발생한다. 난 default 템플릿 필터에서 조용한 실패 처리를 하지 않는 상황을 만났다. default 템플릿 필터는 대개 다음과 같이 사용한다.

{{ empty_var|default:'비었수다' }}

나도 비슷하게 사용했다.

{{ apple.attr3|default:lemon.attrdict.color }}

Django 템플릿 엔진 동작에 익숙하다면 다음과 같이 동작하길 기대(예상)한다.

  1. apple.attr3가 없으면 lemon.attrdict['color']를 대신 출력
  2. lemon.attrdictcolor 키가 없으면 결국 아무것도 출력하지 않고 Silent failure.

딱히 Exception이 발생할만한 로직이 아니고, default 필터 함수를 봐도 인자 두 개 받아서 return value or arg로 동작하는 것 뿐이다. 다시 말해 default 템플릿 필터인 Python 함수는 두 개 인자를 받는데, 첫 번째 인자로 받는 | 앞에 있는 apple.attr3가 있으면 해당 객체를 반환하고, 없으면 두 번째 인자로 받는 : 뒤에 있는 lemon.attrdict.color를 반환한다.

하지만 실제로는 VariableDoesNotExist 예외 오류가 발생한다. 이 예외는 with 템플릿 태그로 해결하면 된다.

with 템플릿 태그로 VariableDoesNotExist 예방

with 템플릿 태그는 Silent failure 처리를 해주니 with 템플릿 태그로 만든 임시 템플릿 변수인 colour엔 출력할(render) 게 없는 빈 객체가 할당이 된다. with 템플릿 태그를 안 쓴 경우엔 Silent failure를 해주는 놈이 없다보니 Exception이 그대로 나버린 것이다. 이 문제가 까다로운 이유는 Django 디버깅 화면에서는 문제가 있는 템플릿 줄(line)을 가리키지 않고 Exception이 발생한 Django 소스를 보여주는 데 있다. 평범한 속성명이나 키 이름을 쓰다가는 고생하기 십상이다.

흔히 겪는 상황은 아닐 것 같다. 나는 모델에 JSONField를 썼고, 이 모델필드의 dict 객체에 특정 키(위 예제 기준으로는 colour키)가 없어서 발생한 거였다. 뷰 함수에서 넘겨주는 템플릿 변수 이름대로라면 금방 발견했을 것 같다.

삼천포 요약 : 변수 네이밍을 괴랄하게 하면 디버깅에 도움이 된다.


시트로엥 C4 칵투스 두 달 소감.

올해 한국에 출시한 시트로엥 C4 칵투스를 샀다.

  • 운행 기간 : 두 달
  • 운행 거리 : 약 1,600km
  • 평균 연비 : 리터 당 18~19km

총평

재밌는 차다. 개성이 강해서 호불호가 극명하게 갈릴 것 같다. 난 선호에 가깝다. 점수를 매기자면 5점 만점에 4점. 30대 3~4인 가정에 추천한다.

특성

1. 연비

도심 내 평속 40~60km 정도일 때 연비는 리터 당 17~18km정도로 측정된다. 고속도로 타서 평속 80~90km로 달리면 연비는 22~25km까지 찍는다. 2016년 11월 기준으로 서울 시내 버스 요금이 기본 요금 1,200원에 10km 초과 시 5km 마다 100원씩 가산되고 경유가 리터 당 대략 1,200~1,300원이니까 연비만 놓고 보면 버스 요금보다 저렴하다.

연비 효율을 높이려는 노력이 곳곳에서 보인다. 먼저 차가 멈추면 엔진을 껐다가 출발할 때 다시 켜는 기능을 들 수 있다. 차량 동작을 완전히 끈 게 아니라 엔진만 끄고, 제동 페달에 발을 떼는 순간 빠르게 다시 시동을 건다. 반응성이 좋아서 불편하진 않다. 하루에 두세 시간 정도 운전하면 에코 유지 시간이 몇 십 분 정도 된다. 몇 십 분 주행을 안 한 셈이니 꽤 연류를 아낀 것이다.

2. 디자인

눈에 띄는 디자인이다. 선이 또렷하고 야무지게 생겼다. 특히 얼굴이 재밌게 생겨서 사람들이 흘깃 눈길 한 번씩은 보낸다. (`^´) 이렇게 생겼달까?

자동차 외관에 플라스틱류 소재가 이렇게 많이 사용된 건 처음 본다. 그 유명한(?) 옆면 에어범퍼도 그렇고, 차 전면부나 후면부 곳곳에도 다소 말랑한 플라스틱 소재가 붙어 있다. 좋고 나쁘고를 떠나서 특이하긴 하다. 색깔도 다양하고 강렬한데, 외형이 워낙 특이해서 난 무난하게 흰색을 골랐다. 흰색이 예쁘다.

내부 인테리어도 아늑하고 귀여운 편이다.

3. 공간, 크기

차체는 작은 편이다. 키나 덩치가 큰 사람에겐 다소 좁게 느껴질 것이다.

그런데 안쪽 공간은 생각보다 여유롭다. 넓다기 보다는 여유감이 있어 답답하지 않다는 뜻이다. 앞좌석은 자잘한 조작 장치를 버튼식으로 바꾸고 대시보드 디스플레이에 몰아 넣어서 변속 레버 부근 공간이 여유롭다. 보조석은 에어백을 상단으로 옮기고, 앞쪽 가방식 수납 공간에 소지품을 넣을 수 있다.

뒷좌석은 다소 좁게 느껴질 여지가 있는데, 자잘한 장치를 쳐내서 공간이 깔끔하고(휑하고) 전방과 상향 시야가 탁 트여서 심리상 편안함을 준다. 하지만 키가 큰 사람이 앉기엔 좁은 건 어쩔 수 없다. 뒷좌석엔 isofix 방식인 유아 카시트를 두 대 장착할 수 있으며, 고리 구멍이 표시되어 있어 연결하기 쉽다. 카시트 두 개 놓으면 뒷좌석은 사실상 공간이 남지 않는다.

트렁크는 생활 물품을 담고 다니기 괜찮은 정도이며 넓은 편은 아니다. 나는 유모차, 자동차 용품 등을 넣고 다니는데, 장 본 물품을 상자 하나에 담아서 트렁크에 담을 정도는 된다. 유모차를 넣고 다니기엔 괜찮지만, 유모차를 싣고 장을 많이 본 날엔 좁다.

뒷좌석 창문은 바깥쪽으로 살짝 열린다. 싫어하는 사람이 꽤 있을 것 같은데, 난 오히려 마음에 든다. 뒷좌석엔 유아 카시트 장착하고 꼬맹이를 앉히기 때문에 아예 꼬맹이가 활짝 열지 못하는 게 마음 편하다. 아이가 타지 않을 때에는 대부분 나 혼자 차를 타거나 동승자 한 명이 보조석에 앉기 때문에 뒷좌석 창문 열 일이 없다. 뒷좌석 창문이 아래로 열리지 않아서 뒷좌석 문짝 안쪽의 수납 공간이 넓다. 어느 정도로 넓냐면 하루 외출하는 데 쓸 아이 기저귀, 소형 우유팩, 간식통을 담을 수 있다. 아이가 용변을 봐서 돌돌 만 기저귀 서너 개는 넉넉히 들어간다.

4. 에어범퍼

예쁜 외관에 눈에 띄게 전혀 다른 재질로 된 에어범퍼가 차 옆면에 꽤 넓게 자리잡고 있다. 내 차는 흰색이고 에어범퍼는 검정색인데, 흰색 몸체는 유광이고 에어범퍼는 무광이라 더 이질감이 크다. 딱 문콕 당할 위치에 에어범퍼가 있어서 문콕 스트레스가 거의 사라진다. 실제로 문콕을 몇 번 당한 것 같은데, 에어범퍼와 바퀴 부근 플라스틱류 소재에 찍힌 자국이 생겼더라. 차체 철판 면은 깨끗하고.

차 옆면이 어딘가에 닿을 때 가장 먼저 에어범퍼가 닿아서 차체 흠집에 대응할 여지가 있다. 좁은 공간을 우회전하며 들어가다 차 오른쪽 부위가 입구 기둥에 닿는 사고(?)가 발생한 적이 있다. 차체가 뭔가에 닿는 느낌이 들자마자 더 진입하지 않고 후진해서 차를 뺐는데, 에어범퍼가 닿아서 차체엔 아무런 흠집이 생기지 않았다. 에어범퍼가 없었다면 흠집이 생겼거나 최악의 경우 찌그러졌을 것이다.

5. 편의 기능

크루즈 기능을 처음 써봤는데 설명으로 들었을 때보다 편했다. 일정 속도로 꾸준히 달리는 장거리 이동 중에 무척 유용했지만, 피곤한 운전 상황은 도심 운전인데 도심 안에서는 쓸모가 없어서 아쉬웠다. 크루즈 기능을 쓰면서 자동운전 기능을 더 기대하게 됐다. 다음 차는 자동운전 기능이 있는 걸로 사야겠다.

주유구는 차 열쇠로 열어야 한다. 실내에서 원격으로 열지 못한다. 이건 호불호가 갈리지 않을 것 같다. 매우 불편하다. 열쇠를 빼서 주유원에게 주면 시동이 꺼졌으므로 창문을 닫지 못한다. 그래서 직접 주유하는 셀프 주유소를 이용하는 편인데, 그래도 싫다.

하드웨어 조작 장치가 몇 개 없다. 대부분 대시보드에 터치 방식으로 옮겨 넣었다. 그래서 공간이 여유로운데, 날씨가 추워지면 터치를 제대로 인식 못하는 경우가 있다. 운전 중엔 가능한 다른 조작을 해서는 안 되긴 하지만 그래도 운전 중에 냉난방을 조절하는 등 뭔가를 조작하는 경우가 왕왕 있는데, 화면 터치로 해야해서 불편한 경우가 생기곤 한다.

변속 관련 하드웨어 조작 장치도 마찬가지여서 변속 막대가 아예 없다. 대신 운전(D), 후진(R), 정차(N) 변속 제어를 큼직한 버튼 세 개가 맡는다. 발렛 파킹하는 사람들이 당황하곤 한다.

벤치식 좌석과 패브릭 시트(?)가 꽤 편안하다. 몸이 푹 잠기는 편안함은 아니고 배기는 부위 없이 안정된 편안함이다.

창문을 세척할 때 분무액은 와이퍼에서 직접 분사하는 방식인데, 세척액이 시야를 가리는 시간이 매우 짧다. 처음엔 세척액이 제대로 분무되지 않은 줄 알 정도이다.

Apple Carplay 기능이나 Android Auto 기능을 지원하지 않는다. 아쉽다.

6. 주행

내가 좋아하는 소리를 내진 않는다. 난 조용하고 얌전한 차를 좋아하며, 다소 묵직한 운전감을 선호한다. 이 차는 가볍다. 차체가 가벼운 편이기도 하지만, 엔진 소리나 운전감이 좋게 말하면 경쾌하고 나쁘게 말하면 가볍다. 차가 가볍다보니 고속 주행 시 바닥에서 올라오는 진동과 소리가 크다. 더욱이 정차 시 에코 상태로 전환되어 엔진이 꺼져서 아주 조용한 상황을 경험하게 되기 때문에 더 비교된다.

완전 자동 변속 장치가 아니고, 수동 변속을 기반으로 자동 변속을 해주는 변속 방식이다. 연비는 좋은데, 변속될 때 차가 꿀렁거리는 수동 변속의 단점을 안고 있다. 잘 운전하면(?) 꿀렁거림을 줄일 수 있지만, 변속 중 꿀렁거리는 상황을 피하기 힘든 경우도 있다. 1단으로 오르막길 오르는데 RPM이 상승해서 2단으로 바뀌면 순간 꿀렁, 그런데 곧 힘이 부쳐서 다시 1단으로 바뀌면 또 꿀렁. 고속도로에서 6단으로 달리던 중 추월하려고 가속 페달을 밟으면 RPM이 부족해서 5단으로 변속되어 꿀렁, 근데 평지라서 금방 RPM이 진정되면 다시 6단으로 바뀌어서 또 꿀렁. 수동 변속 차량을 오래 운전해서 수동 변속을 좋아하는데도 이 변속 패턴은 짜증나곤 한다.

보기보다 힘이 괜찮다. 수동 변속 특유의 RPM 장난질로 튀어나가는 힘도 제법이고, 시속 130km까지는 뻗어나가는 힘도 괜찮다. 근데 고속 주행에 적합한 차는 아니긴 하다.

방향 전환과 제동하는 느낌도 묵직해서 밀리지 않아 안전감 있다. 급곡선을 돌 때는 차 중안 부근이, 급제동을 걸 때는 차 엉덩이 부근이 땅으로 꾹 눌리는 느낌이 든달까? 주행감은 상당히 가벼워서 과속 방지턱도 퉁퉁 튀듯 넘는 기분이 드는데, 방향 전환과 제동은 묵직해서 비교되어 재밌다. 상대적으로 묵직한 게 아니라 정말 묵직하다. 새 차라 그런가? 마음에 든다.

사람은 자신의 몸이 움직이는 공간을 인지하고 있다. 부딪히지 않고 좁은 틈을 지나갈 수 있는지, 줄넘기를 하는 저 사람과 어느 정도 떨어져야 안전한지 무의식 중에 파악한다. 운전석에 앉으면 자신의 몸을 기준으로 인식된 개인 공간이 차체만큼 확장된다. 마치 차체가 내 몸이 된 것처럼. 이 인지와 실제 차체와 동기화되는 비율이 높을수록 마음이 편하고 안정되는데, 동기화 수준을 떨어뜨리는 요소가 몇 가지 있다. 운전석 기준으로 사각, 후사경에서 인식되는 차 뒷쪽 공간과 실제 차 엉덩이 위치의 위치 차이, 운전석에서 보이는 후드(보닛) 거리와 실제 후드 길이 차이 등이다. 이 차는 내 체형에 잘 맞아서 시야가 좋다. 차체가 SUV에 비해선 낮고, 세단보다는 아주 조금 더 높아서 운전석 높이가 적당하다. 그리고 사각(죽은 시야각)이 거의 없다.