>웹 프론트엔드 >JS 튜토리얼 >프론트엔드 개발자 인터뷰 질문

프론트엔드 개발자 인터뷰 질문

Barbara Streisand
Barbara Streisand원래의
2024-11-17 07:33:03750검색

Frontend Developer Interview Questions

1. 질문: JavaScript에서 var, let, const의 차이점을 설명할 수 있나요?

정답:

  • var: 함수 범위이며 재선언 및 업데이트가 가능합니다. 호이스팅됩니다. 이는 해당 선언이 컴파일 타임에 해당 범위의 맨 위로 이동됨을 의미합니다.
  • let: 블록 범위이며 업데이트할 수 있지만 동일한 범위 내에서 다시 선언할 수는 없습니다. var와 같은 방식으로 끌어올려지지 않습니다.
  • const: 블록 범위이며 업데이트하거나 다시 선언할 수 없습니다. 값은 선언 시 할당되어야 합니다. 상수를 정의하는 방법을 제공합니다.
  • let 및 const를 사용하면 범위 문제 및 우발적인 재할당으로 인한 일반적인 버그를 방지하는 데 도움이 됩니다.

2. 질문: React 애플리케이션에서 상태를 어떻게 관리합니까?

정답:

React의 상태 관리는 다음을 통해 처리할 수 있습니다.

  • 로컬 구성 요소 상태: 간단한 시나리오에 useState 또는 클래스 구성 요소 상태를 사용합니다.
  • 컨텍스트 API: 소품 드릴링 문제의 경우 모든 수준에서 수동으로 소품을 전달하지 않고 구성 요소 트리를 통해 데이터를 전달합니다.
  • 상태 관리 라이브러리: 전역 상태가 필요한 복잡한 애플리케이션을 위한 Redux, MobX 또는 Zustand 등.
  • 후크: 상태 저장 논리를 캡슐화하고 재사용하기 위한 사용자 정의 후크입니다.
  • React Query 또는 SWR: 서버 상태 관리용.
  • 선택은 애플리케이션의 복잡성과 요구 사항에 따라 달라집니다.

3. 질문: Virtual DOM은 무엇이고 React는 이를 어떻게 사용하나요?

정답:

  • Virtual DOM은 React 구성요소에 의해 생성된 실제 DOM 요소를 메모리 내 표현한 것입니다.
  • 구성 요소의 상태가 변경되면 React는 Virtual DOM 트리를 업데이트합니다.
  • 그런 다음 실제 DOM을 업데이트하는 데 필요한 최소한의 변경 사항 집합(차이)을 효율적으로 계산합니다.
  • 이 프로세스는 비용이 많이 드는 작업인 DOM의 직접 조작을 줄여 성능을 향상시킵니다.

4. 질문: JavaScript에서 이벤트 위임을 설명하세요.

정답:

  • 이벤트 위임은 이벤트 버블링을 활용하여 개별 노드가 아닌 DOM의 더 높은 수준에서 이벤트를 처리합니다.
  • 각 하위 요소에 이벤트 리스너를 추가하는 대신 단일 이벤트 리스너를 상위 요소에 연결합니다.
  • 하위 요소에서 이벤트가 트리거되면 상위 요소로 버블링되어 캡처되고 처리될 수 있습니다.
  • 이 접근 방식은 특히 동적으로 추가된 요소를 처리할 때 성능을 향상시키고 코드 관리를 단순화합니다.

5. 질문: 웹 구성 요소는 무엇이며 사용자 정의 요소와 어떤 관련이 있나요?

정답:

  • 웹 구성요소는 재사용 가능하고 캡슐화된 HTML 태그를 생성할 수 있는 표준화된 API 세트입니다.
  • 다음으로 구성됩니다:
    • 맞춤 요소: 새로운 유형의 HTML 요소를 정의합니다.
    • Shadow DOM: 스타일과 마크업에 대한 캡슐화를 제공합니다.
    • HTML 템플릿: 재사용 가능한 템플릿을 정의할 수 있습니다.
  • 사용자 정의 요소는 개발자가 사용자 정의 동작과 스타일을 사용하여 자신만의 HTML 태그를 만들 수 있도록 하는 웹 구성 요소의 핵심 부분입니다.

6. 질문: CSS 특정성은 어떻게 작동하나요?

정답:

CSS 특수성은 여러 규칙이 적용될 수 있는 경우 요소에 적용되는 스타일 규칙을 결정합니다.

  • 인라인 스타일(스타일 속성)은 가장 높은 특이성을 갖습니다.
  • ID(#id)는 클래스보다 구체성이 높습니다.
  • 클래스, 속성 및 의사 클래스(.class, [type="text"], :hover)는 중간 정도의 특이성을 갖습니다.
  • 요소 및 의사 요소(div, ::before)는 특이도가 가장 낮습니다.
  • 특이성은 선택자의 조합을 기준으로 계산됩니다. 구체성이 동일하면 정의된 마지막 규칙이 우선 적용됩니다.

7. 질문: JavaScript의 Promise란 무엇이며 콜백과 어떻게 다릅니까?

정답:

  • Promise는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체입니다.
  • 비동기 결과를 처리하기 위해 .then(), .catch() 및 .finally()와 같은 메서드를 제공합니다.
  • 프라미스와 콜백:
    • 프라미스를 사용하면 더 깔끔하고 유지 관리가 용이한 비동기 코드와 더 나은 오류 처리가 가능합니다.
    • 콜백은 중첩된 콜백으로 인해 코드를 읽고 유지하기 어렵게 만드는 '콜백 지옥'으로 이어질 수 있습니다.
  • Promise는 기존 콜백에 비해 코드 가독성과 관리 효율성을 향상시킵니다.

8. 질문: JavaScript에서 클로저가 어떻게 작동하는지 설명해 주실 수 있나요?

정답:

  • 클로저는 외부 변수를 기억하고 액세스할 수 있는 함수입니다.
  • 이는 함수가 자체 범위, 외부 함수 범위 및 전역 범위에서 변수에 액세스할 수 있음을 의미합니다.
  • 함수가 생성될 때마다 함수 생성 시 클로저가 생성됩니다.
  • 데이터 개인정보 보호 및 개인 메서드 에뮬레이션에 유용합니다.

9. 질문: 웹 애플리케이션의 성능을 어떻게 최적화합니까?

정답:

  • HTTP 요청 최소화: 파일을 결합하고 스프라이트를 사용합니다.
  • 비동기 로딩: 스크립트에 비동기 및 연기를 사용합니다.
  • 캐싱: 브라우저 캐싱을 구현하고 CDN(콘텐츠 전송 네트워크)을 사용합니다.
  • 이미지 최적화: 이미지를 압축하고 WebP와 같은 차세대 형식을 사용하세요.
  • 코드 분할: Webpack과 같은 도구를 사용하여 지연 로딩을 위해 코드를 분할합니다.
  • 렌더링 차단 리소스 감소: CSS 및 JavaScript 전달을 최적화합니다.
  • 성능 모니터링: 감사를 위해 Lighthouse 및 Chrome DevTools와 같은 도구를 사용하세요.

10. 질문: 프로그레시브 웹 앱(PWA)에서 서비스 워커의 목적은 무엇인가요?

정답:

  • 서비스 워커는 웹 애플리케이션과 네트워크 간의 프록시 역할을 합니다.
  • 자산을 캐싱하고 네트워크 요청을 가로채서 오프라인 지원과 같은 기능을 활성화합니다.
  • 백그라운드 동기화 및 푸시 알림을 지원합니다.
  • 리소스 캐싱 및 검색을 제어하여 성능을 향상시킵니다.

11. 질문: CSS의 Box 모델을 설명하세요.

정답:

  • CSS 상자 모델은 모든 HTML 요소를 둘러싸는 상자입니다.
  • 다음으로 구성됩니다:
    • 콘텐츠: 텍스트나 이미지와 같은 실제 콘텐츠입니다.
    • 패딩: 테두리 내부 콘텐츠 주변의 공간입니다.
    • 테두리: 패딩과 콘텐츠를 둘러싸는 테두리입니다.
    • 여백: 이 요소와 다른 요소 사이의 경계 외부 공간입니다.
  • 레이아웃과 디자인을 위해서는 박스 모델에 대한 이해가 필수적입니다.

12. 질문: React의 HOC(고차 컴포넌트)가 무엇인가요?

정답:

  • HOC는 구성요소를 가져와 새 구성요소를 반환하는 함수입니다.
  • 구성요소 간에 공통 기능을 공유하는 데 사용됩니다.
  • HOC는 props 주입, 상태 관리, 부작용 처리 등을 할 수 있습니다.
  • 사용 예: const EnhancedComponent = withFeature(WrappedComponent);

13. 질문: 웹 애플리케이션에서 접근성을 어떻게 보장합니까?

정답:

  • 시맨틱 HTML 사용: HTML 요소의 올바른 사용.
  • ARIA 속성: 필요한 경우 추가 컨텍스트를 제공하세요.
  • 키보드 탐색: 키보드를 통해 모든 대화형 요소에 액세스할 수 있는지 확인하세요.
  • 대비 및 가독성: 적절한 색상 대비와 텍스트 크기를 사용하세요.
  • 이미지용 대체 텍스트: 설명적인 대체 속성을 제공하세요.
  • 테스트: 접근성 테스트 도구와 보조 기술을 사용하세요.

14. 질문: CORS(Cross-Origin Resource Sharing)란 무엇이며 어떻게 작동하나요?

정답:

  • CORS는 다른 도메인에서 요청한 리소스를 허용하거나 제한하는 보안 기능입니다.
  • 응답 읽기가 허용되는 출처를 지정하는 HTTP 헤더를 추가하여 작동합니다.
  • 브라우저는 CORS 정책을 시행하며 서버는 Access-Control-Allow-Origin과 같은 적절한 헤더를 포함해야 합니다.
  • 실행 전 요청(OPTIONS 메서드)은 권한을 확인하기 위한 복잡한 요청에 사용됩니다.

15. 질문: 웹 애플리케이션에서 지연 로딩을 구현하는 방법을 설명하세요.

정답:

  • 이미지 및 미디어:
    • 에서 loading="lazy" 속성을 사용하세요. 태그.
    • 미디어가 뷰포트에 들어갈 때 미디어를 로드하도록 Intersection Observer API를 구현합니다.
  • 코드:
    • Webpack이나 다른 번들러와 함께 동적 가져오기를 사용하세요.
    • React에서는 구성 요소 수준 코드 분할을 위해 React.lazy() 및 Suspense를 사용합니다.
  • 혜택:
    • 초기 로드 시간과 성능이 향상됩니다.
    • 불필요한 데이터 사용량을 줄여줍니다.

16. 질문: JavaScript에서 ==와 ===의 차이점은 무엇인가요?

정답:

  • == (추상 동등): 유형이 다른 경우 유형 강제를 수행한 후 값을 비교합니다.
  • === (엄격한 동일성): 유형 강제 없이 값과 유형을 모두 비교합니다.
  • 예:
    • 0 == '0'이 참입니다.
    • 0 === '0'은 거짓입니다.
  • 형식 강제로 인한 예상치 못한 결과를 방지하려면 일반적으로 ===를 사용하는 것이 좋습니다.

17. 질문: 비동기 코드에서 오류를 어떻게 처리합니까?

정답:

  • 약속: 거부를 처리하려면 .catch()를 사용하세요.
  • 비동기/대기: try...catch 블록으로 대기 호출을 래핑합니다.
  • 전역 오류 처리기: 처리되지 않은 약속 거부의 경우.
  • 오류 경계(React): 구성 요소 트리에서 오류를 포착합니다.
  • 올바른 오류 처리는 더 나은 사용자 경험과 더 쉬운 디버깅을 보장합니다.

18. 질문: 반응형 디자인의 개념과 구현 방법을 설명해주세요.

정답:

  • 반응형 디자인은 웹사이트가 다양한 화면 크기와 기기에 적응하도록 보장합니다.
  • 구현:
    • CSS Flexbox 또는 Grid와 함께 유연한 그리드 레이아웃을 사용하세요.
    • 뷰포트 크기에 따라 스타일을 조정하는 미디어 쿼리를 구현합니다.
    • 백분율, em 또는 rem과 같은 상대 단위를 사용하세요.
    • 다양한 화면 해상도에 맞게 이미지를 최적화하세요.
  • 테스트: 브라우저 개발자 도구와 물리적 장치를 사용하여 응답성을 테스트합니다.

19. 질문: CSS 전처리기란 무엇이며 왜 사용합니까?

정답:

  • CSS 전처리기는 변수, 중첩, 믹스인, 함수와 같은 기능을 추가하여 CSS의 기능을 확장합니다.
  • 예에는 Sass, Less, Stylus 등이 있습니다.
  • 혜택:
    • 코드 재사용성 및 유지 관리성.
    • 대규모 CSS 코드베이스를 더 쉽게 관리할 수 있습니다.
    • 브라우저 호환성을 위해 표준 CSS로 컴파일할 수 있습니다.

20. 질문: React에서 불변성의 개념과 그 중요성을 설명해 주실 수 있나요?

정답:

  • 불변성은 데이터가 생성된 후에는 데이터를 변경할 수 없음을 의미합니다.
  • React에서 불변성은 다음과 같은 이유로 중요합니다.
    • 예측 가능한 상태 변경을 허용합니다.
    • React는 얕은 비교를 수행할 수 있으므로 성능 최적화에 도움이 됩니다.
    • 의도하지 않은 부작용을 방지합니다.
  • 구현:
    • Object.asset 또는 스프레드 연산자와 같은 데이터 구조의 새 복사본을 반환하는 메서드를 사용하세요.
    • 복잡한 데이터 구조에는 Immutable.js와 같은 라이브러리를 활용하세요.

21. 질문: Webpack은 무엇이고 왜 사용되나요?

정답:

  • Webpack은 JavaScript 애플리케이션용 모듈 번들러입니다.
  • 용도:
    • 브라우저에서 사용할 JavaScript 파일을 번들로 묶습니다.
    • 로더를 통해 CSS, 이미지, 글꼴과 같은 자산을 처리하고 번들로 묶습니다.
    • 코드 분할 및 지연 로딩을 활성화합니다.
    • 확장된 기능을 위한 플러그인을 지원합니다.
  • 혜택:
    • 종속성을 효율적으로 관리합니다.
    • 생산을 위한 자산을 최적화합니다.

22. 질문: 교차 사이트 스크립팅(XSS) 공격을 어떻게 방지합니까?

정답:

  • 입력 삭제: 서버 측의 모든 사용자 입력을 정리하고 검증합니다.
  • 출력 인코딩: 브라우저에서 렌더링하기 전에 사용자 입력을 이스케이프합니다.
  • 콘텐츠 보안 정책(CSP): 악성 스크립트를 방지하기 위해 신뢰할 수 있는 콘텐츠 소스를 정의합니다.
  • 인라인 스크립트 방지: JavaScript 코드를 외부 파일에 유지하세요.
  • HTTPOnly 쿠키 사용: JavaScript를 통한 쿠키 액세스를 방지합니다.
  • 정기 보안 감사: 최신 보안 모범 사례를 받아보세요.

23. 질문: 단일 페이지 애플리케이션(SPA) 사용의 장점과 단점은 무엇입니까?

정답:

  • 혜택:
    • 전체 페이지를 다시 로드할 필요 없이 원활한 사용자 경험을 제공합니다.
    • 초기 로드 후 성능이 향상되었습니다.
    • 모바일과 같은 경험을 더 쉽게 만들 수 있습니다.
  • 단점:
    • SEO 문제. 단, 서버 측 렌더링으로 완화할 수 있습니다.
    • 초기 로드 시간이 더 길어질 수 있습니다.
    • 브라우저 기록 관리는 복잡할 수 있습니다.
  • SPA와 기존 다중 페이지 애플리케이션 중에서 선택하는 것은 프로젝트 요구 사항에 따라 다릅니다.

24. 질문: JavaScript에서 this 키워드는 어떻게 작동하나요?

정답:

  • this는 현재 기능을 실행하고 있는 객체를 의미합니다.
  • 컨텍스트:
    • 전역 컨텍스트: 이는 전역 개체(브라우저의 창)를 나타냅니다.
    • 객체 메소드: 메소드를 소유한 객체를 의미합니다.
    • 이벤트 핸들러: 이벤트를 트리거한 DOM 요소를 나타냅니다.
    • 화살표 기능: 자신만의 this가 없습니다. 그들은 그것을 둘러싸는 범위로부터 상속받습니다.
  • 이를 이해하는 것은 JavaScript의 객체 지향 프로그래밍에 매우 중요합니다.

25. 질문: RESTful API와 GraphQL의 차이점을 설명하세요.

정답:

  • RESTful API:
    • HTTP 메소드와 엔드포인트를 사용하여 리소스에 액세스하세요.
    • 데이터는 리소스를 중심으로 구성됩니다.
    • 데이터를 과도하게 가져오거나 적게 가져오는 결과를 초래할 수 있습니다.
  • GraphQL:
    • 단일 엔드포인트를 사용합니다.
    • 고객은 필요한 데이터를 정확하게 지정합니다.
    • 네트워크 요청 수를 줄입니다.
    • 스키마와 해석기가 필요합니다.
  • 선택 요소:
    • 프로젝트 요구 사항, 데이터 복잡성 및 팀 전문성.

26. 질문: 대규모 React 애플리케이션에서 스타일을 어떻게 관리합니까?

정답:

  • CSS 모듈: 구성 요소에 대한 로컬 범위의 CSS 클래스입니다.
  • 스타일 구성 요소: JavaScript 내에서 CSS를 작성할 수 있는 CSS-in-JS 라이브러리입니다.
  • Sass/Less: 고급 CSS 기능을 위해 전처리기를 사용하세요.
  • BEM 방법론: 명명 규칙 및 구성을 위한 것입니다.
  • 테마 지정: 컨텍스트나 라이브러리를 사용하여 일관된 스타일을 제공하세요.
  • 접근 방식은 팀 선호도와 프로젝트 요구 사항에 따라 다릅니다.

27. 질문: React Hooks는 무엇이고 왜 도입되었나요?

정답:

  • React Hooks는 클래스를 작성하지 않고도 상태 및 기타 React 기능을 사용할 수 있게 해주는 함수입니다.
  • 공통 후크:
    • 상태 관리를 위한 useState.
    • 부작용에는 useEffect를 사용하세요.
    • 컨텍스트 API에 대한 useContext입니다.
  • 소개 이유:
    • 기능 구성 요소의 상태 저장 논리를 단순화합니다.
    • 복잡한 수업을 피하세요.
    • 사용자 정의 후크를 통해 더 나은 코드 재사용이 가능합니다.

28. 질문: 단일 페이지 애플리케이션에서 인증을 구현하는 방법을 설명하세요.

정답:

  • 토큰 기반 인증:
    • 안전하게 저장된 JWT를 사용하세요(HTTP 전용 쿠키 선호).
    • 토큰을 받고 저장하는 로그인 흐름을 구현합니다.
  • 경로 보호:
    • 인증된 경로를 보호하려면 상위 구성 요소나 경로 보호 장치를 사용하세요.
  • 백엔드 통합:
    • 인증을 위한 API 엔드포인트를 설정하세요.
    • 서버 측에서 토큰을 검증합니다.
  • 보안 고려 사항:
    • XSS 및 CSRF 공격으로부터 보호합니다.
    • HTTPS를 사용하여 데이터 전송을 암호화합니다.

29. 질문: 함수형 프로그래밍이란 무엇이며 JavaScript에 어떻게 적용되나요?

정답:

  • 함수형 프로그래밍은 계산을 수학 함수의 평가로 취급하는 패러다임입니다.
  • 핵심 개념:
    • 순수한 함수: 부작용이 없으며 동일한 입력에 대해 동일한 출력을 반환합니다.
    • 불변성: 데이터는 생성 후에도 변경되지 않습니다.
    • 일급 함수: 함수는 값으로 처리됩니다.
    • 고차 함수: 다른 함수를 취하거나 반환하는 함수입니다.
  • 자바스크립트:
    • 맵, 축소, 필터, 함수 표현식과 같은 기능을 갖춘 함수형 프로그래밍을 지원합니다.

30. 질문: 브라우저 호환성과 폴리필을 어떻게 처리합니까?

정답:

  • 기능 감지: Modernizr 또는 유사한 도구를 사용하여 지원되지 않는 기능을 감지하세요.
  • 폴리필: 이전 브라우저(예: Babel 폴리필)의 최신 기능을 복제하는 스크립트를 포함합니다.
  • 변환: Babel과 같은 도구를 사용하여 ES6 코드를 ES5로 변환하세요.
  • 점진적 향상: 모든 브라우저에서 작동하는 기능을 구축하여 가능한 경우 향상시킵니다.
  • 테스트: 다양한 브라우저와 기기에서 정기적으로 테스트하세요.
  • 사용 가능 여부: 구현 전 기능 지원을 확인하세요.

31. 질문: 사용자 중심 디자인이란 무엇입니까?

정답:

사용자 중심 디자인은 모든 단계에서 사용자를 염두에 두고 디자인하는 것입니다. 실제 사용자 요구 사항에 초점을 맞추고 이를 개발 프로세스 전반에 걸쳐 참여시킴으로써 제품이 성공적이고 사용자 친화적이며 대상 고객의 실제 요구 사항을 충족할 가능성이 높아집니다.

32. 질문: 콜백 지옥이 무엇인가요?

정답:

콜백 지옥(Callback Hell)은 중첩된 콜백이 여러 개 있어서 코드를 읽고 유지 관리하기 어려운 안티 패턴을 말합니다. Promises, Async/Await 및 적절한 코드 구조를 사용하면 더 깔끔한 비동기 코드를 작성하고 콜백 지옥에 빠지는 것을 피할 수 있습니다.

33. 질문: SOLID는 무엇을 의미하나요?

정답:

SOLID 원칙은 개발자가 관리, 확장 및 확장이 쉬운 소프트웨어를 구축하기 위한 지침 역할을 합니다. 이러한 원칙을 따르면 시간의 시험을 견디고 새로운 요구 사항에 원활하게 적응할 수 있는 강력한 시스템을 만들 수 있습니다.

34. 질문: 클릭재킹이란 무엇인가요?

정답:

'UI 교정 공격'이라고도 알려진 클릭재킹은 공격자가 사용자를 속여 사용자가 인식하는 것과 다른 것을 클릭하도록 유도하여 잠재적으로 승인되지 않은 작업을 수행하거나 기밀 정보를 노출시키는 악의적인 기술입니다.

예:

  • 사용자가 가짜 '동영상 재생' 버튼 아래에 은행 웹사이트의 로그인 버튼을 로드하는 악성 웹사이트를 방문했습니다. 사용자가 동영상을 재생하기 위해 클릭하면 실제로는 뱅킹 사이트의 로그인 버튼을 클릭하는 것이므로 의도하지 않은 동작이 시작될 가능성이 있습니다.

35. 질문: JavaScript에서 강제 변환이란 무엇인가요?

정답:

JavaScript의 강제 변환은 한 데이터 유형에서 다른 데이터 유형으로 값을 변환하는 프로세스를 나타냅니다. JavaScript는 암시적(자동)과 명시적(수동)이라는 두 가지 방법으로 강제를 수행합니다.

36. 질문: JavaScript에서 IIFE란 무엇입니까?

정답:

IIFE(Immediately Invoked Function Expression)는 정의되자마자 실행되는 JavaScript 함수입니다. 코드에 비공개 범위를 제공하는 디자인 패턴입니다.

37. 질문: CSS의 그리드 시스템이란 무엇입니까?

정답:

CSS의 그리드 시스템은 개발자가 복잡하고 반응성이 뛰어난 웹 디자인을 쉽게 만들 수 있는 레이아웃 프레임워크입니다. 콘텐츠를 행과 열로 정렬하는 구조화된 방법을 제공하여 반응성이 뛰어나고 유연한 레이아웃을 쉽게 만들 수 있습니다.

38. 질문: JavaScript의 네임스페이스란 무엇입니까?

정답:

JavaScript에서 네임스페이스는 개발자가 이름 충돌을 방지하고 전역 범위를 깔끔하게 유지하기 위해 관련 코드를 고유한 이름으로 그룹화할 수 있는 컨테이너입니다. JavaScript에는 다른 언어처럼 내장된 네임스페이스 지원이 없으므로 개발자는 개체, 모듈 또는 IIFE(즉시 호출 함수 표현식)를 사용하여 네임스페이스를 만듭니다.

39. 질문: JavaScript에서 use strict 지시문의 사용은 무엇입니까?

정답:

use strict 지시문은 오류가 발생할 가능성이 적은 깔끔한 JavaScript 코드를 작성하는 데 사용됩니다. 변수를 선언하지 않고 할당하거나 같은 이름을 가진 다른 매개변수를 함수에 전달하는 등의 일반적인 코딩 오류를 잡아냅니다.

40. 질문:

정답:

defer 또는 async 속성을