A LIST Apart: For People Who Make Websites

폼 유효성 검사에 대한 준비

by Ryan Seddon

웹이 태어났을때부터, 폼의 유효성 검사는 세심한 주의를 요하는 작업이었습니다. 가장 먼저 있었던 것은, 서버사이드에서 유효성 검사를 한 후 오류의 요약을 보여 주는 것이었습니다. 그후 클라이언트 사이드에서 결과들을 인라인으로 점검할 수 있도록 진화했습니다. 이제, 우리는 HTML5와 CSS3라는 거인과 마주치게 되었습니다: HTML5의 폼에 관한 장은 새로운 입력 유형들과 속성을 제공하여 유효성 검사 제약조건이 가능하도록 하고 있습니다. CSS3의 기본 UI 모듈은 다양한 가상클래스들을 제공하여 그러한 유효성검사 상태들에 스타일을 부여하고, 사용자의 행동에 기반하여 필드의 외관을 변경할 수 있게 하고 있습니다. 두가지를 살펴보고, 이를 더해서 퍽 넓은 브라우저 지원이 가능한 CSS 기반 유효성검사를 만들어 보도록 합시다.

Form validation has been a finicky business since the web was born. First came the server-side validation error summary. Then we evolved to client-side validation to verify results inline. Now, we have the marching giant that is HTML5 and CSS3: HTML5’s forms chapter offers new input types and attributes that make validation constraints possible. CSS3’s basic UI moduleprovides several pseudo-classes to help us style those validation states and change a field’s appearance based on the user’s actions. Let’s take a look at combining the two to create a CSS-based form validator that has fairly broad browser support.

폼 필드들을 어떻게 입력할 것인지 사용자에게 실시간으로 많은 것을 알려줄수록, 사용자가 실수를 할 가능성이 줄어 들 것입니다. 크롬 4+, 사파리 5+, 오페라 9.6+ 같은 CSS3 UI 가상 클래스를 지원하는 브라우저들에서 CSS3 폼 유효성검사 예제를 살펴보십시오. HTML5의 폼 속성들과, CSS3 UI 가상클래스를 이용해서 CSS 기반의 유효성 검사를 만들었습니다. 그것이 어떻게 동작하는지 살펴봅시다.

The more we can guide a user on how to complete a form field in real-time, the less likely they are to make mistakes. Look at the CSS3 form validation examplein a browser that supports the CSS3 UI pseudo-classes, such as Chrome 4+, Safari 5+ or Opera 9.6+. I use CSS3 UI pseudo-classes and HTML5 form attributes to do CSS-based validation. Let’s see how that works.

CSS3 UI 가상클래스

UI 모듈은 몇가지의 가상 클래스를 가지고 있으며, 여러가지 상태에 있는 폼 필드들에 이러한 것을 이용해서 스타일을 줄 수 있습니다.

The UI module has several pseudo-classes that help to style form fields in various states.

위의 데모에는 required, invalid, valid를 사용해서 CSS 기반의 유효성 검사를 수행했습니다.

In the demo above, I use the required, invalid, and validpseudo-classes to accomplish the CSS validation:

input:focus:required:invalid {
  background: pink url(ico_validation.png) 379px 3px no-repeat;
}
input:required:valid {
  background-color: #fff;
  background-position: 379px -61px;
}

잘못된 필드가 포커스를 가졌을 때에만 그것이 잘못되있음을 지적하고자 하기 때문에, focus가상 클래스를 사용해서 잘못된 경우의 스타일을 지정하고 있습니다. (페이지가 로드되자마자 필수적인 필드 모두가 잘못된 것으로 표시되어 있다면 그것은 물론 형편없는 디자인입니다)

Since we only want to denote that a field is invalid once it has focus, we use the focuspseudo-class to trigger the invalid styling. (Naturally, flagging all required fields as invalid from the start would be a poor design choice.)

필수적이지만 잘못 입력된 필드로 포커스를 가져가면 느낌표 그래픽을 보여주어서 사용자에게 뭔가를 입력해야 한다고 경고합니다. 필드의 유효성검사 제약조건이 만족되면, valid 가상 클래스가 동작합니다. 이제 focus가상 클래스를 제거해서, 올바른 필드임을 나타내는 녹색 마크가 남아있도록 합니다.

Bringing focus to an invalid required field triggers the style to show the exclamation graphic, which alerts the user that something needs to be entered. Once the field’s validation constraints are satisfied, the valid pseudo-class triggers. Now, we remove the focuspseudo-class so that the green tick that indicates a correct field will remain.

위에 나열한 모든 가상 클래스들은 이름 그대로입니다. in-rangeout-of-rangemin, max 속성과 함께 사용해야 합니다. 예를 들어, 사용자가 out-of-range 해당하는 값을 입력했다면, 가상 클래스를 사용해서 스타일이 이 상태를 반영하도록 할 수 있습니다. 미찬가지로, 같은 것을 in-range에 대해서도 할 수 있습니다.

All the pseudo-classes listed above are self explanatory. The in-range and out-of-range pseudo-classes should be used in conjunction with the min and max attributes, whether on a range input, a number field, or any other types that accept those attributes. For example, if a user enters a value that is out-of-range, we can use the pseudo-class to change the styling to reflect that state; likewise, we can do the same for in-rangevalues.

현재로서는 오페라만이 범위 가상클래스를 지원합니다. 다른 브라우저들도 곧 지원할 것입니다.

Only Opera supports the range pseudo-classes at the moment. Other browsers will follow soon.

도움이 될 만한 타입과 속성들

HTML5의 폼은 다른 입력 타입들 - email, url, number 같은 것들도 소개하고 있습니다. 예를 들어, email은 유효한 이메일 주소가 입력되었을 경우에만 valid 가상클래스를 발생시킵니다. numberurl에서도 같습니다. url 제약 조건은 브라우저들마다 달릅니다. 오페라에서는, "http://" 라고 입력하면 url 필드를 올바른 것으로 판단합니다. 크롬에서는 "http://w" 라고 입력해야 하고, 사파리에서는 "http:" 까지만 입력해도 그것을 올바른 url로 인식합니다.

HTML5 forms also introduce new input types such as email, url, and number. For example, email only triggers the valid pseudo-class when the user enters a valid e-mail address; the same is true for number and url. Url constraint validation differs among browsers. In Opera, typing “http://” flags the url field as valid. In Chrome typing “http://w” flags it valid, while simply typing “http:” in Safari flags a url as valid.

유효성 검사를 도와주는 몇가지 속성들이 있습니다. placeholder, required, maxlength, pattern, min, max, step이 그러한 것들입니다.

There are also a few attributes which help validation such as placeholder, required, maxlength, pattern, min, max, and step:

<input id="postcode" name="postcode" type="number" min="1001" max="8000"
maxlength="4" required />

postcode 필드는 새로운 입력 타입인 number를 사용하며, 몇가지 새로운 속성들도 사용하고 있습니다. 호주에서는, 우편번호는 4개의 숫자로만 이루어질 수 있으므로 maxlength 속성을 사용해서 그렇게 제한합니다. 또한 minmax를 사용해서 우편번호가 가질 수 있는 최소값과 최대값을 경계를 정하고 있습니다. required속성은 이름만 봐도 알겠군요.

The postcode field uses the new number type and a few of the new attributes. In Australia, a postcode can only be four digits so we set the maxlength attribute to restrict it. We also want to restrict how high or low the postcode can be so we use min and max attributes to set boundaries. The requiredattribute is self explanatory.

step 속성을 이용해서, minmax 속성을 가진 필드에 더 많은 제약을 가할 수 있습니다. step 속성이 기본값은 1이므로 최소값과 최대값 사이에 있는 어떠한 숫자라도 - 최소한 1 씩만 증가한다면 - 올바른 것으로 판단됩니다. step의 값을 100으로 변경하면, 사용자가 입력하는 숫자도 100 단위여야 합니다. 예를 들어 내가 우편번호 필드의 step을 100으로 지정했다면, 1001은 유효한 입력입니다. 1101, 1201, 1301 등도 그렇지요.

We can use the step attribute to further restrict a field with min and max. By default, step is set to one, so any number between the min and max values incremented by at least one validates. Changing stepto 100 validates between the set range if the value the user entered is an increment of 100. For example, if I set the step attribute to 100 on my postcode field, 1001 will be a valid entry, as will 1101, 1201, 1301, etc.

패턴을 찾아냅니다

좀 더 명시적인 조건, 예를 들어 가장 기본적인 전화번호 같은, 에서 invalid 가상 클래스를 활성화시키려면, pattern속성을 이용해서 그 필드에 정규식을 적용할 수 있습니다.

To trigger the invalid pseudo-class on more specific conditions, such as a rudimentary phone number, we can use the patternattribute which allows us to apply a regular expression to the field.

<input type="tel" id="tel" name="tel" pattern="\d{10}" placeholder=
"Please enter a ten digit phone number" required />

위의 정규식은 간단한 것입니다. 그것의 의미는, '정확히 10개의 숫자만 받아들일 것입니다. 그것 이외에는 안됩니다' 라는 것입니다. 이렇게 하면, 필드는 정규식이 만족되지 않다면 항상 잘못된 것입니다. 사용자에게 작은 힌트를 제공하기 위해 placeholder 속성을 사용한 것을 눈여겨 보십시오.

The regular expression above is a simple one. It says, “I will only accept exactly ten digits and nothing else.” That way, the field will always be invalid until the regular expression requirements are met. Notice how I have used the placeholder attribute to give the user a small hint.

비밀번호 필드에 사용한 것과 같은 복잡한 정규식을 적용함으로서 pattern속성의 강력함을 사용할 수 있습니다:

We can really push the power of the patternattribute by applying a more complex regular expression as I do on the password field:

<input id="password" name="password" type="password" title="Minimum 8
characters, one number, one uppercase and one lowercase letter" required
pattern="(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*" />

더 안전한 비밀번호를 만들도록 강제하기 위해서 사용자가 비밀번호 필드에 입력하는 것을 제한하는 좀 더 명시적인 조건을 갖고 있으므로, 위에서 보이는 것 같은 복잡한 정규식을 적용하였습니다. 비밀번호는 최소한 8자리 이상이어야 하며, 숫자와 소문자, 대문자를 한개 이상씩 포함하고 있어야 합니다.

Since we have specific conditions that restrict what the user can enter, forcing them to create a more secure password, we set up a complex regular expression as shown above. The password must be at least eight characters long, contain one number, one lowercase letter, and one uppercase letter.

사용자가 이러한 조건을 만족할 수 있도록 돕기 위해서, title 속성을 사용해서 정확히 어떤 제약조건이 있는 것인지 이해할 수 있도록 하고 있습니다. 여기에서는 placeholder속성을 사용하지 않았는데, 그 속성은 짧은 힌트에 적용해야 하며 이러한 긴 설명은 적합하지 않기 때문입니다.

To help a user meet these conditions, we use the title attribute to help them understand exactly what the requirements are. We don’t use the placeholder attribute here, as it needs more explanation and placeholdershould only be used for short hints.

도움이 되는 힌트들 추가

만약 사용자가 필드들 위로 절대 마우스를 올리지 않고 오직 탭을 통해서만 이동한다면, 사용자는 title 속성으로 제공되는 추가적인 설명들을 알아차리지 못할 것입니다. 당신은 전화번호, 우편번호, 비밀번호 필드처럼 추가적인 지시사항이 필요한 필드에서 도움이 되는 힌트가 제공되었음을 보았을 것입니다.

If the user never hovers over the field, and instead tabs through them, they may never notice the extra instructions in the titleattribute. You may notice that on the phone, postcode, and password fields, a helpful hint appears when a field needs extra instructions.

<input id="password" type="password"  />
<p class="validation01">
  <span class="invalid">Minimum 8 characters, one number, one uppercase 
letter and one lowercase letter</span>
  <span class="valid">Your password meets our requirements, thank you.
</span>
</p>

위의 마크업은 invalid, valid힌트 박스 모두에 추가적인 컨테이너를 가지고 있습니다. 이렇게 하면, 필드가 올바르지 않을 경우에 사용자를 도울 수 있는 추가적인 정보를 담게 될 것입니다. 필드가 올바르다면, 우리가 제공하는 메시지와 녹색 마크가 나타나서 사용자가 필드를 올바르게 채워 넣었음을 알려줄 것입니다.

The markup above has an extra container with both the invalid and validhint boxes. This way, when the field is invalid, it will contain the extra information to help the user. When they get it right, our message and the green tick reassures them that they have filled it out correctly.

.validation01 {
  background: red;
  color: #fff;
  display: none;
  font-size: 12px;
  padding: 3px;
  position: absolute;
  right: -110px;
  text-align: center;
  top: 0;
  width: 100px;
}
input:focus + .validation01 {
  display: block;
}
input:focus:required:valid + .validation01 {
  background: green;
}
input:focus:required:valid + .validation01 .invalid {
  display: none;
}
input:focus:required:invalid + .validation01 .valid {
  display: none;
}

필드의 현재 상태에 따라 적합한 형제sibling단서를 사용함으로서 가상 클래스를 바꾸어서 도움이 되는 힌트를 보이거나 감출 수 있습니다. 일단 필드가 바르게 채워지면, 배경은 녹색으로 변하며 올바르다는 메시지가 나타나게 됩니다.

To show or hide the helpful hint, depending on what state the field is in, we can target the field by chaining the pseudo-classes, using the adjacent sibling combinator to target the correct hint. Once the field has been filled out correctly, the background changes to green and the valid message displays.

현재의 접근 방법에 대해, UX 관점에서 생각할 것

어떤 필드가 필수적인 필드인 동시에, 꼭 만족되어야 할 추가적인 조건이 있는 경우 - 예를 들어, 어떤 필드가 필수적이고 또한 이메일 필드인 경우 - invalid 가상 클래스가 동작하는 방법에 큰 불만이 있습니다. 그러한 조건이 만족되지 않을 경우 필드는 항상 잘못된 것이기 때문에, 항상 invalid 스타일 일 것입니다. 이 경우, 필드는 즉시 잘못된 것으로 간주될 것이며, 심지어 사용자가 아무것도 입력하지 않았을 경우에도, 잘못되었다고 표시될 것입니다. 이러한 것이 우리가 필드에 invalid 스타일을 적용할 때 focus가상 클래스도 확인해야 하는 이유입니다. 하지만 이러한 것은 최적은 아닙니다: 사용자가 유효성 검사 규칙을 만족시키지 않고 그 필드에서 떠났을 경우, 다시 그 필드로 포커스를 주기 전에는 뭔가 잘못된 점이 있음을 전혀 표시하지 못합니다.

There is one main gripe with how the invalid pseudo-class currently works when a field is required and has additional conditions that must be satisfied—for example, when a field is required and its type is email. Because the field is always invalid until its conditions are met, it will pick up the invalid styles. In this case, the field would be instantly invalid, and marked red with errors even before the user has entered anything. That᾿s why we use the focuspseudo-class to show the invalid styles only when a field is in focus. This isn’t optimal: if a user moves away from the field without meeting its validation requirements, the field will not indicate that something is wrong until the user brings focus back to it.

이러한 것에 대해 indeterminate 가상 클래스를 제안하고자 합니다. indeterminate 가상 클래스는 라디오 버튼과 체크박스에서 사용할 수 있습니다. 기술적으로는, 필수적이라는 것 이외의 조건을 가진 필드가 비어 있을 경우 그 필드는 올바른것도, 잘못된것도 아닌 불분명한것입니다. 이러한 아이디어를 이용해서, 페이지가 로드되자마자 잘못된 것으로 표시되는 현상을 고치고, 유효성 검사 상태에 의해서 필드에 적합한 스타일을 입힐 수 있을 것입니다.

A proposed solution to this would be to add the indeterminate pseudo-class available on radio and checkbox inputs. Technically, a field that has more conditions than being required when it’s empty is neither valid nor invalid but rather indeterminate. This idea would fix the instant invalid issue and allows us to optimally style the field depending on its validation state.

추가적으로, 자바스크립트를 이용하지 않고도 몇가지 멋진 기능들을 만들 수 있습니다. 필드가 어떤 상태에 있는지, 필수적인 요소인지, 정규식을 이용해서 특정한 패턴에 맞게 입력해야 한다고, 최소값과 최대값을 알려줄 수 있습니다. 그 외에도 많습니다. 하지만 그러한 것으로도 충분하지 않다면? 더 확장하고 싶다면? 다행히도, HTML5의 폼에 관한 장은 제약조건 검사 API역시 명시하고 있습니다.

Additionally, we can accomplish some pretty comprehensive functionality without JavaScript. We can tell what state a field is in, if it’s required, tell it to conform to a certain pattern with regular expressions, specify minimum and maximum values, and much more. But what if that’s not enough? What if we want to take it further? Well we’re in luck as the HTML5 forms chapter also specifies the constraint validation API.

제약조건 검사 API

새로운 속성, 입력 타입, CSS3 가상클래스들과 더불어, HTML5의 폼에 관한 장은 간단한 자바스크립트 API를 제공해서, 내장되어 있는 편리한 메서드, 속성, 이벤트들을 가지고 유효성 검사 능력을 확장할 수 있도록 하고 있습니다. 이러한 것을 가지고 강력해진 데모를 한번 보십시오.

Alongside all the new attributes, input types, and CSS3 pseudo-classes, the HTML5 forms chapter also specifies a simple JavaScript API that allows us to extend form validation capabilities further with some handy built-in methods, attributes, and events. Take a look at the updated demo, which hooks into the constraints validation API.

각각의 폼 필드들은 validity 라는 새로운 속성을 갖고 있습니다. 이 속성은 요소의 유효성 상태를 나타내는, ValidityState 객체를 반환합니다. ValidityState 객체는 몇가지 불리언 속성들을 포함하는데, 이러한 것을 가지고 현재 요소가 어떠한 유효성 상태에 있는지를 식별하게 됩니다. 기본적으로는, 그것들은 참/거짓 값의 연속이며, 개발자들은 이것을 가지고 이 필드에서 무엇이 잘못되었는지 정확하게 알 수 있습니다.역주

Each form field has a new attribute called validity. The validity attribute returns a ValidityState object which represents an element’s current validity state(s). The ValidityStateobject contains several Boolean attributes which identify which validity state the current element is in. Basically, they’re a series of true/false answers that tell a developer exactly what is wrong with the field:

더 많은 것들이 있습니다

invalid이벤트도 편리한 기능입니다. 이것은 필드가 올바르지 않을때 발생합니다. 따라서 이러한 것에 기능을 덧붙일 수 있는데, 우리의 경우에는 현재의 상태에 맞게끔 필드(들)의 스타일을 바꿀 것입니다.

The invalidevent is another handy feature. It will be invoked by the field when it is still invalid. So we can attach behaviour to it and, in our case, change the field(s) styling to reflect their current state.

이에 더해, checkValidity() 메서드를 각각의 필드, 혹은 폼 전체에 실행하고 true/false를 얻을 수 있습니다. 이 메서드를 실행하면 또한 자동적으로 invalid이벤트를 하나의 필드, 혹은 모든 필드에 발생시킬 수 있습니다.

Additionally, the checkValidity() method can be executed on either an individual field or the form as a whole, and returns true or false. Executing the method will also programmatically fire the invalidevent for all invalid fields, or, if executed on a single field, only for that element.

데모를 봅시다

앞의 데모를 제약조건 검사 API를 이용해서 강화해 봅시다. Luke Wroblewski의 웹 폼의 인라인 유효성 검사에서 배운 것들과 우리가 찾아낸 것을 적용하면, 이러한 아이디어를 적용해서 적절한 인라인 유효성검사를 만들 수 있습니다.

Let’s take our previous demo and enhance it with the constraint validation API. Taking what we’ve learned from Luke Wroblewski’s Inline Validation in Web Formsand our own findings, we can apply these ideas to our form to create the optimal inline validation experience.

가장 먼저 고쳐야 할 것은, 잘못된 필드가 즉시 에러 스타일을 갖게 되는 것입니다. 사용자가 요구사항에 맞는 입력을 하지 않은 것을 즉시 알리는 대신, 사용자가 그 필드로부터 떠날때까지 기다렸다가 알려주기로 합니다.

The first thing we can fix is the instant error styling of an invalid field. Rather than instantly styling the field to indicate the user hasn’t met the requirements, we wait until they move away from the field to show any issues.

필드가 포커스를 갖고 있는 동안에 요구사항이 충족되면, 그 필드가 정확하게 입력되었음을 사용자에게 즉시 알리도록 합니다. input이벤트를 사용하면 필드가 유효하게 입력되었는지 알 수 있습니다. 그런 경우, 스타일에 즉시 그것이 반영되도록 업데이트합니다.

If they meet the requirements while the field is still in focus, we let our user know instantly that the field is correct. We do this by attaching the inputevent to check to see if the field is valid. When it is, we update the styles to reflect it straight away.

필드에 입력된 값이 잘못된 상태에서 사용자가 다른 필드로 이동할 경우, blur이벤트에서 필드의 정합성을 검사하고 에러 스타일을 적용해서 사용자가 뭔가 잘못되었음을 알도록 합니다. 요구사항이 충족될때까지 에러 스타일을 계속 유지하도록 합니다.

If a field has incorrect values, and the user moves to the next field, the blurevent will check the field’s validity and then apply the error styles to let the user know something is wrong. It will retain the error styling until the requirements are met.

오래된 브라우저에서는?

여지껏 논의한 모든 기능들은 아주 새로운 것들입니다. 좋기는 하지만, 오래된 브라우저들도 지원해야 하는 현실에는 그다지 적합하지 않은 것 같습니다. 내가 만들어 둔 스크립트를 이용해서 이런 상황에 편리하게 대응할 수 있을 것입니다.

All the topics discussed are fairly new and browser support, while good, wouldn’t cut it in a real-world production environment where we must support older browsers. That’s where the scriptI wrote comes in handy.

HTML5의 폼에 관한 장과 제약조건 검사 API를 지원하지 않는 브라우저에 대해, 스크립트에서 그러한 기능을 에뮬레이트합니다. 이러한 기능을 지원하는 브라우저에서는, 스크립트는 그러한 지원을 감지하고 네이티브한 기능을 이용합니다. 스크립트를 추가하여 더욱 업데이트된 데모를 보도록 합시다. 이것을 IE나 파이어폭스에서 사용해 보고, 직접 지원하는 브라우저와 마찬가지로 동작하는지 확인해 보십시오.

For browsers that don’t support the HTML5 forms chapter and the constraint validation API, the script emulates that functionality. For browsers that support these features, the script detects support and hooks into the native functionality. Let’s take a look at the further updated demowith the new script added in. Try it in IE or Firefox to see it work like the native supporting browsers do.

브라우저 지원

이 스크립트는 다음의 브라우저들에서 테스트되었으며, 동작합니다.

This script has been tested and works in the following browsers:

스크립트에서는 다음의 기능들을 에뮬레이트합니다:

The following features are emulated in the script:

충분한 유효성 검사!

HTML5의 폼과 CSS3 UI 모듈에 대한 브라우저 지원은 점점 나아지고 있습니다. 오페라 9 버전은 웹 폼 2.0이 HTML5에 추가되기 전부터 구현함으로서 앞서 나갔는데, CSS3 UI 모듈은 9.6부터 지원하고 있습니다. 크롬은 버전 4부터 지원하고 있으며, 사파리는 최근의 버전 5에서부터 지원합니다. 파이어폭스는 버전 4의 베타부터 지원할 예정이며, IE9는 프리뷰 빌드 중 어떤 단계에서부터 지원을 시작할 것 같습니다.

Browser support for HTML5 forms and the CSS3 UI module is starting to improve. Opera 9 lead the way by implementing Web Forms 2.0before it merged into the HTML5 forms chapter, but it has only supported the CSS3 UI module since version 9.6. Chrome has had support since version 4, Safari recently shipped it in version 5, Firefox is due to add support in a forthcoming beta of version 4, and IE9, if they continue their progress, should also have support in one of their preview builds.

CSS3의 새로운 모듈과 HTML5의 폼을 이용해서 놀라운 일들을 할 수 있습니다. 브라우저 지원이 점점 좋아짐에 따라, 이러한 종류의 테크닉들이 폼 유효성 검사의 복잡함을 간단하게 이끌 수 있는 방법이 되어 가고 있습니다.

We can do some amazing things with the new modules and chapters from CSS3 and HTML5 respectively. As browser support improves, these sorts of techniques become a viable option that can cater to the simple and the complex nature of form validation.

1. 아래의 속성들은 “잘못되었을 때” 참을 반환합니다. 혼동하기 쉬우니 조심하세요.
2. HTML5의 폼 유효성 검사는 아직 “working draft” 상태이며, 이러한 API는 이후 변경될 소지가 많습니다. 이러한 방향으로 “응용할 수도 있다” 라고만 받아들이실 것을 권합니다.돌아가기

댓글 (1648)