답변:
즉, 발에 총을 쏘는 것을 방지합니다. 예전 JSP 시대에는 JSP 파일에 Java 코드가 뿌려진 것이 매우 일반적이었으며 코드가 흩어져 있기 때문에 리팩토링이 훨씬 더 어려워졌습니다.
콧수염처럼 디자인 에 따라 템플릿의 로직을 방지 하면 로직을 다른 곳에 배치해야하므로 템플릿이 깔끔하게 정리됩니다.
또 다른 장점은 관심사 분리 측면에서 생각해야한다는 것입니다. 컨트롤러 또는 논리 코드는 데이터를 UI로 보내기 전에 데이터 마사지 를 수행해야합니다 . 나중에 템플릿을 다른 템플릿으로 전환하면 (다른 템플릿 엔진을 사용하기 시작한다고 가정 해 보겠습니다) UI 세부 정보 만 구현해야했기 때문에 전환이 쉬울 것입니다 (템플릿에 논리가 없기 때문에).
나는 거의 외롭다는 느낌이 들지만, 나는 확실히 반대 진영에있다. 템플릿에서 비즈니스 로직을 혼합 할 수있는 가능성이 프로그래밍 언어의 모든 기능을 사용하지 않는 충분한 이유라고 생각하지 않습니다.
논리없는 템플릿에 대한 일반적인 주장은 프로그래밍 언어에 대한 전체 액세스 권한이있는 경우 템플릿에없는 논리를 혼합 할 수 있다는 것입니다. 칼을 사용하면자를 수 있기 때문에 숟가락으로 고기를 썰어 야한다는 추론과 비슷합니다. 이것은 매우 사실이지만 신중하게 사용하더라도 후자를 사용하면 훨씬 더 생산적이 될 것입니다.
예를 들어 콧수염을 사용하는 다음 템플릿 스 니펫을 고려하세요 .
{{name}}:
<ul>
{{#items}}
<li>{{.}}</li>
{{/items}}
</ul>
나는 이것을 이해할 수 있지만 다음 ( 밑줄 사용 )이 훨씬 더 간단하고 직접적이라는 것을 알았습니다.
<%- name %>:
<ul>
<% _.each(items, function(i){ %>
<li><%- i %></li>
<% }); %>
</ul>
즉, 논리없는 템플릿이 장점이 있다는 것을 이해합니다 (예 : 변경없이 여러 프로그래밍 언어에서 사용할 수 있음). 저는 이러한 다른 장점이 매우 중요하다고 생각합니다. 나는 그들의 논리없는 성격이 그들 중 하나라고 생각하지 않습니다.
name
와 items
자바 스크립트를 포함 할 수 있습니다.
콧수염은 논리가 없습니까?
그렇지 않나요?
{{#x}}
foo
{{/x}}
{{^x}}
bar
{{/x}}
이것과 꽤 비슷합니까?
if x
"foo"
else
"bar"
end
그리고 그것은 프리젠 테이션 로직과 꽤 비슷 하지 않습니까?
if x > 0 && x < 10
) ... 따라서 Mustache를 논리 유무에 관계없이 사용할 수 있지만 그것은 당신에게 달려 있습니다. 결국 그것은 단지 도구 일뿐입니다.
논리없는 템플릿은 채우는 방법이 아닌 채울 구멍이 포함 된 템플릿입니다. 논리는 다른 곳에 배치되고 템플릿에 직접 매핑됩니다. 이러한 우려의 분리는 이상적입니다. 템플릿은 다른 논리 또는 다른 프로그래밍 언어로 쉽게 빌드 할 수 있기 때문입니다.
로부터 수염 설명서 :
if 문, else 절 또는 for 루프가 없기 때문에 "논리없는"이라고 부릅니다. 대신 태그 만 있습니다. 일부 태그는 값으로 대체되고 일부는 아무것도 표시되지 않고 다른 태그는 일련의 값으로 대체됩니다. 이 문서에서는 다양한 유형의 Mustache 태그에 대해 설명합니다.
동전의 뒷면은 비즈니스 로직을 프레젠테이션에서 제외시키려는 필사적으로 시도하면 모델에 많은 프레젠테이션 로직을 넣게된다는 것입니다. 일반적인 예는 테이블의 교대 행에 "홀수"및 "짝수"클래스를 배치하려는 경우 일 수 있습니다. 이는 뷰 템플릿에서 간단한 모듈로 연산자로 수행 할 수 있습니다. 그러나 뷰 템플릿이 그렇게 할 수 없다면 모델 데이터에 어떤 행이 홀수 또는 짝수인지 저장해야 할뿐만 아니라 템플릿 엔진이 얼마나 제한되어 있는지에 따라 모델을 오염시켜야 할 수도 있습니다. 실제 CSS 클래스의 이름으로. 뷰는 모델과 완전히 분리되어야합니다. 그러나 모델은 뷰에 구애받지 않아야하며, 이러한 "논 로직"템플릿 엔진 중 많은 부분이이를 잊어 버리게 만듭니다. 논리는 두 곳 모두에 있습니다.실제로 어디로 가는지 정확하게 결정합니다. 프레젠테이션 문제입니까, 아니면 비즈니스 / 데이터 문제입니까? 100 % 깨끗한 시각을 갖기위한 노력의 일환으로, 오염은 눈에 잘 띄지 않지만 똑같이 부적절한 곳으로 떨어집니다.
다른 방향으로 되돌아가는 움직임이 증가하고 있으며, 상황이 더 합리적인 중간 지점의 어딘가에 집중되기를 바랍니다.
그것은 당신의 템플릿을 더 깨끗하게 만들고, 그것은 당신이 적절하게 단위 테스트를 할 수있는 곳에 로직을 유지하도록 강요합니다.
이 대화는 중년의 승려들이 핀 끝에 얼마나 많은 천사가 들어갈 수 있는지에 대해 토론 할 때처럼 느껴집니다. 즉, 종교적이고 쓸데없고 잘못 집중된 느낌이 들기 시작했습니다.
사소한 폭언이 계속됩니다 (무시해도됩니다) :
계속 읽고 싶지 않다면 .. 위의 주제에 대한 저의 짧은 대답은 다음과 같습니다. 논리없는 템플릿에 동의하지 않습니다. 나는 그것을 극단주의의 프로그래밍 형태라고 생각합니다. :-) :-)
이제 내 폭언이 본격적으로 계속됩니다. :-)
많은 아이디어를 극단적으로 취하면 결과가 우스꽝스러워 진다고 생각합니다. 그리고 때때로 (즉이 주제) 문제는 우리가 "잘못된"아이디어를 극단으로 가져가는 것입니다.
보기에서 모든 논리를 제거하는 것은 "우스꽝스럽고"잘못된 생각입니다.
잠시 뒤로 물러서십시오.
우리가 스스로에게 물어봐야 할 질문은 왜 논리를 제거 하는가? 개념은 분명 관심사 분리입니다 . 가능한 한 비즈니스 로직과 별도로 뷰 처리를 유지하십시오. 왜 이러나요? 이를 통해 뷰 (다른 플랫폼 : 모바일, 브라우저, 데스크톱 등)를 교체 할 수 있으며 제어 흐름, 페이지 시퀀스, 유효성 검사 변경, 모델 변경, 보안 액세스 등을보다 쉽게 교체 할 수 있습니다. 보기 (특히 웹보기)에서 제거하면보기를 훨씬 더 읽기 쉽고 유지 관리하기 쉽습니다. 나는 그것을 얻고 그것에 동의합니다.
그러나 우선 순위는 관심사 분리에 있어야합니다. 100 % 논리없는보기가 아닙니다. 뷰 내의 논리는 "모델"을 렌더링하는 방법과 관련되어야합니다. 제가 아는 한, 뷰의 논리는 완벽합니다. 비즈니스 로직이 아닌 뷰 로직을 가질 수 있습니다.
예, 코드 로직과 뷰 로직을 거의 또는 전혀 분리하지 않고 JSP, PHP 또는 ASP 페이지를 작성했을 때 이러한 웹 앱의 유지 관리는 절대적으로 악몽이었습니다. 저를 믿으세요. 저는이 괴물들 중 일부를 만들고 유지했습니다. 유지 보수 단계에서 나와 동료 방식의 오류를 실제로 (내장 적으로) 이해했습니다. :-) :-)
따라서 높은 (업계 전문가)의 칙령은 프론트 뷰 컨트롤러 (핸들러 또는 액션 [웹 프레임 워크 선택]에 전달되는)와 같은 것을 사용하여 웹 앱을 구성해야하며 뷰에는 코드가 없어야합니다. . 뷰는 멍청한 템플릿이되었습니다.
그래서 저는 일반적으로 위의 정서에 동의합니다. 칙령 항목의 세부 사항이 아니라 칙령 뒤에있는 동기, 즉 관점과 비즈니스 논리 사이의 관심사를 분리하려는 욕구입니다.
제가 참여한 한 프로젝트에서 우리는 논리없는 관점의 아이디어를 터무니없는 극단까지 따르려고했습니다. 모델 객체를 html로 렌더링 할 수있는 자체 개발 템플릿 엔진이있었습니다. 단순한 토큰 기반 시스템이었습니다. 아주 간단한 이유 때문에 끔찍했습니다. 때로는 뷰에서이 HTML 스 니펫을 표시할지 여부를 결정해야합니다. 결정은 일반적으로 모델의 일부 값을 기반으로합니다. 뷰에 논리가 전혀없는 경우 어떻게합니까? 글쎄 당신은 할 수 없습니다. 나는 이것에 대해 우리 건축가와 몇 가지 중요한 논쟁을했다. 우리의 뷰를 작성하는 프론트 엔드 HTML 사람들은 이것에 직면했을 때 완전히 방해를 받았으며 다른 단순한 목표를 달성 할 수 없었기 때문에 매우 스트레스를 받았습니다. 그래서 템플릿 엔진에 간단한 IF 문 개념을 도입했습니다. 나는 당신에게 그에 따른 안도감과 평온함을 설명 할 수 없습니다. 템플릿의 간단한 IF- 문 개념으로 문제가 해결되었습니다! 갑자기 템플릿 엔진이 좋아졌습니다.
그렇다면 우리는 어떻게이 어리석은 스트레스 상황에 빠졌을까요? 우리는 잘못된 목표에 집중했습니다. 우리는 규칙을 따랐습니다. 당신의 견해에 논리가 없어야합니다. 그건 틀렸어요. 나는 "경험의 법칙"이 당신의 견해에서 논리의 양을 최소화해야한다고 생각한다. 그렇지 않으면 실수로 비즈니스 로직이 뷰에 포함되도록 허용 할 수 있으므로 우려 사항 분리를 위반합니다.
"뷰에 논리가 없어야합니다"라고 선언하면 "좋은"프로그래머인지 쉽게 알 수 있습니다. (그게 당신의 선량 척도라면). 이제 위의 규칙을 사용하여 중간 정도의 복잡한 웹 앱을 구현해보십시오. 그렇게 쉽게 할 수는 없습니다.
나에게있어서 논리의 규칙은 그렇게 명확하지 않으며 솔직히 그것이 내가 원하는 곳이다.
뷰에서 많은 논리를 볼 때 코드 냄새를 감지하고 뷰에서 대부분의 논리를 제거하려고합니다. 비즈니스 논리가 다른 곳에 있는지 확인하려고합니다. 우려 사항을 분리하려고합니다. 하지만 제가 보기에서 모든 논리를 제거 해야 한다고 말하는 사람들과 대화를 시작하면 , 제가 위에서 설명한 것과 같은 상황에서 끝날 수 있다는 것을 알고있는 것처럼 광신주의에 불과합니다.
나는 내 폭언을 끝냈다. :-)
건배,
데이비드
논리없는 템플릿에 대해 제가 생각 해낸 가장 좋은 주장은 클라이언트와 서버 모두에서 똑같은 템플릿을 사용할 수 있다는 것입니다. 그러나 당신은 정말로 논리없는 것이 필요하지 않습니다. 단지 그것만의 "언어"를 가지고있는 것입니다. 콧수염이 무의미하다고 불평하는 사람들의 의견에 동의합니다. 고마워요.하지만 저는 거물이고 당신의 도움 없이도 템플릿을 깨끗하게 유지할 수 있습니다.
또 다른 옵션은 node.js를 사용하여 클라이언트와 서버 모두에서 지원되는 언어, 즉 서버에서 자바 스크립트를 사용하는 템플릿 구문을 찾는 것입니다. 또는 therubyracer와 같은 것을 통해 js 인터프리터와 json을 사용할 수 있습니다.
그런 다음 지금까지 제공된 예제보다 훨씬 깨끗하고 훌륭하게 작동하는 haml.js와 같은 것을 사용할 수 있습니다.
질문이 오래되고 답변이되었지만 2 ¢를 추가하고 싶습니다 (폭언처럼 들릴 수 있지만 그렇지 않은 경우 제한 사항에 관한 문제이며 수용 할 수 없게되는 경우).
템플릿의 목표는 비즈니스 로직을 수행하는 것이 아니라 무언가를 렌더링하는 것입니다. 이제 템플릿에서 필요한 작업을 수행 할 수없는 것과 "비즈니스 로직"이있는 것 사이에는 얇은 경계가 있습니다. 나는 콧수염에 대해 정말 긍정적이고 그것을 사용하려고 노력했지만, 꽤 간단한 경우에 필요한 것을 할 수 없게되었습니다.
데이터의 "마사지"(허용 된 답변의 단어 사용)는 실제 문제가 될 수 있습니다. 단순한 경로조차 지원되지 않습니다 (Handlebars.js가 해결하는 것). 뷰 데이터가 있고 템플릿 엔진이 너무 제한적이기 때문에 무언가를 렌더링하고 싶을 때마다 조정해야한다면 결국에는 도움이되지 않습니다. 그리고 그것은 콧수염이 스스로 주장하는 플랫폼 독립성의 일부를 무너 뜨립니다. 나는 모든 곳에서 마사지 로직을 복제해야한다.
즉, 약간의 좌절과 다른 템플릿 엔진을 시도한 후에 .NET Razor 템플릿에서 영감을 얻은 구문을 사용하는 자체 (... 또 다른 ...)를 만들었습니다. 서버에서 구문 분석 및 컴파일되어 템플릿을 "실행"하기 위해 호출 할 수있는 간단한 자체 포함 JS 함수 (실제로는 RequireJS 모듈)를 생성하여 결과로 문자열을 반환합니다. brad가 제공 한 샘플은 엔진을 사용할 때 다음과 같을 것입니다 (개인적으로 Mustache와 Underscore에 비해 가독성이 훨씬 뛰어납니다).
@name:
<ul>
@for (items) {
<li>@.</li>
}
</ul>
또 다른 논리없는 제한은 Mustache로 부분을 호출 할 때 우리에게 부딪 혔습니다. 부분은 Mustache에서 지원되지만 먼저 전달 될 데이터를 사용자 정의 할 수는 없습니다. 따라서 모듈 식 템플릿을 생성하고 작은 블록을 재사용하는 대신 반복 된 코드가 포함 된 템플릿을 작성하게됩니다.
우리는 JPath라고 부르는 XPath에서 영감을 얻은 쿼리 언어를 구현하여이를 해결했습니다. 기본적으로 /를 사용하는 대신 점을 사용하여 문자열, 숫자 및 부울 리터럴뿐만 아니라 객체 및 배열도 지원합니다 (JSON처럼). 이 언어는 부작용이 없지만 (템플릿에 필수) 새 리터럴 개체를 만들어 필요에 따라 데이터를 "마 사이징"할 수 있습니다.
사용자 지정 가능한 헤더와 행의 작업에 대한 링크가있는 "데이터 그리드"테이블을 렌더링하고 나중에 jQuery를 사용하여 동적으로 행을 추가한다고 가정 해 보겠습니다. 따라서 코드를 복제하지 않으려면 행이 부분적으로 있어야합니다. 그리고 렌더링 할 열과 같은 추가 정보가 뷰 모델의 일부이고 각 행의 해당 작업에 대해 동일한 경우 문제가 시작됩니다. 다음은 템플릿 및 쿼리 엔진을 사용하는 실제 작업 코드입니다.
테이블 템플릿 :
<table>
<thead>
<tr>
@for (columns) {
<th>@title</th>
}
@if (actions) {
<th>Actions</th>
}
</tr>
</thead>
<tbody>
@for (rows) {
@partial Row({ row: ., actions: $.actions, columns: $.columns })
}
</tbody>
</table>
행 템플릿 :
<tr id="@(row.id)">
@for (var $col in columns) {
<td>@row.*[name()=$col.property]</td>
}
@if (actions) {
<td>
@for (actions) {
<button class="btn @(id)" value="@(id)">@(name)...</button>
}
</td>
}
</tr>
JS 코드에서 호출 :
var html = table({
columns: [
{ title: "Username", property: "username" },
{ title: "E-Mail", property: "email" }
],
actions: [
{ id: "delete", name: "Delete" }
],
rows: GetAjaxRows()
})
비즈니스 로직이 없지만 재사용 및 구성이 가능하며 부작용이 없습니다.
row
정적 이름을 사용하는 대신 개체 에서 가져올 속성 이름을 동적으로 지정할 수 있습니다 . 예 $col.property == 'Something'
를 들어 이것이 row.Something
.
다음은 문자 수와 함께 목록을 렌더링하는 3 가지 방법입니다. 첫 번째이자 가장 짧은 것을 제외한 모든 것은 논리없는 템플릿 언어입니다.
CoffeeScript ( 리 액티브 커피 빌더 DSL 포함)-37 자
"#{name}"
ul items.map (i) ->
li i
녹아웃-100 자
<span data-bind="value: name"/>
<ul data-bind="foreach: items">
<li data-bind="value: i"/>
</ul>
핸들 바 / 콧수염-66 자
{{name}}:
<ul>
{{#items}}
<li>{{.}}</li>
{{/items}}
</ul>
밑줄-87 자
<%- name %>:
<ul>
<% _.each(items, function(i){ %>
<li><%- i %></li>
<% }); %>
</ul>
논리없는 템플릿의 약속은 더 넓은 기술을 가진 사람들이 스스로를 쏘지 않고도 논리없는 템플릿을 관리 할 수 있다는 것입니다. 그러나 위의 예에서 볼 수있는 것은 문자열 기반 마크 업에 최소 논리 언어를 추가하면 결과가 덜 복잡하지 않고 더 복잡하다는 것입니다. 또한 구식 PHP를 사용하는 것처럼 보입니다.
분명히 나는 템플릿에서 "비즈니스 로직"(광범위한 계산)을 유지하는 데 반대하지 않습니다. 하지만 디스플레이 로직을위한 1 급 언어 대신 의사 언어를 제공함으로써 대가를 지불한다고 생각합니다. 그저 타이핑하는 것뿐만 아니라, 누군가가 그것을 읽어야 할 상황 전환의 악한 혼합이 필요합니다.
결론적으로, 나는 논리없는 템플릿의 논리를 보지 못하기 때문에 그들의 장점은 나에게 없다고 말하고 싶지만 커뮤니티의 많은 사람들이 그것을 다르게 보는 것을 존경합니다. :)
나는 Brad의 의견에 동의합니다. underscore
스타일이 이해하기 더 쉽습니다. 그러나 나는 통사론 적 설탕이 모든 사람을 기쁘게하지 않을 수도 있음을 인정해야합니다. _.each
다소 혼란스러운 경우 기존 for
루프를 사용할 수 있습니다 .
<% for(var i = 0; i < items.length; i++) { %>
<%= items[i] %>
<% } %>
당신과 같은 표준 구조로 폴백 할 수 있다면 그것은 항상 좋은 for
나 if
. 그냥 사용 <% if() %>
하거나 <% for() %>
동안 Mustache
사용 정도에 대한 신조어 if-then-else
(당신이 문서를 읽을하지 않은 경우 혼란) :
{{#x}}
foo
{{/x}}
{{^x}}
bar
{{/x}}
템플릿 엔진은 중첩 된 템플릿을 쉽게 얻을 수있을 때 유용합니다 ( underscore
스타일).
<script id="items-tmpl" type="text/template">
<ul>
<% for(var i = 0; i < obj.items.length; i++) { %>
<%= innerTmpl(obj.items[i]) %>
<% } %>
</ul>
</script>
<script id="item-tmpl" type="text/template">
<li>
<%= name %>
</li>
</script>
var tmplFn = function(outerTmpl, innerTmpl) {
return function(obj) {
return outerTmpl({obj: obj, innerTmpl: innerTmpl});
};
};
var tmpl = tmplFn($('#items-tmpl').html(), $('#item-tmpl').html());
var context = { items: [{name:'A',{name:'B'}}] };
tmpl(context);
기본적으로 내부 tmpl을 컨텍스트의 속성으로 전달합니다. 그리고 그에 따라 부르십시오. 달콤한 :)
그런데 관심있는 유일한 것이 템플릿 엔진 인 경우 독립형 템플릿 구현을 사용하십시오. 그것은이다 에만 900 자 (4 긴 줄) 축소 된 경우 :