서울시청 클론코딩

핑구·2023년 7월 20일
0

Portfolio

목록 보기
1/5
post-thumbnail

🏛️ Seoul City Hall

👉 사이트명 : 서울특별시청
👉 작업기간 : 2일 ( 23.07.17. ~ 07.18. )
👉 사용언어 : HTML5, CSS3, Jquery
👉 라이브러리 : Swiper.js
👉 분류 : PC 적응형 웹사이트
👉 URL : https://seona-cha.github.io/seoul/


POINT ✨

✔️ background link 위에 link들이 있는 경우 ( a > a ❌ )
✔️ 사용자 편의성을 위한 본문 바로가기 버튼, 태그의 나열순서, key down이벤트, 맨위로 버튼
✔️ window.open()을 이용한 언어선택 셀렉트박스
✔️ swiper 자동재생, 자동정지 기능
✔️ swiper 따로 만든 탭 버튼과 연동


구조 살펴보기 👀

🔍 전체 태그를 wrapper로 감싸주기

wrapper를 사용하는 이유 ? 적응형 사이트에서 최소너비를 보장하기 위함 !

<div class="wrapper">
  <header class="header"> 
  .
  </header>
  
  <main class="container"> 
  .
  .
  </main>
  
  <footer class="footer"> 
  .
  </footer>
</div>
/* 최소너비 보장 */
.wrapper{
	min-width:1152px;
}

🔍 시멘틱 태그를 활용해 영역 구성

~ (시멘틱 태그를 사용하면 SEO 최적화에 유리)
큰 뼈대는 header, main, footer, 내부 각 영역은 section, article, nav 등 각 영역이 담고있는 정보에 대한 의미가 있는 시멘틱 태그를 사용했다.

 <!-- 이벤트 신청 -->
 <section class="sc-board">
 .
 .
 </section>
 <!-- // 이벤트 신청 -->

🔍 article

section들을 모은 영역은 독립적으로 존재할수 있는 article 태그로 감쌀 수 있다.
서울시청 사이트를 클론코딩하면서 서울시미디어 영역을 article로 구성했다.

<article>
  <h2 class="blind">서울시미디어</h2>
  
  <section class="group-video">
  	<h3 class="headline">서울영상</h3>
    .
    .
  </section>
  
  <section class="group-news">
  	<h3 class="headline">서울소식</h3>
    .
    .
  </section>
  
  <section class="group-sns">
  	<h3 class="headline">서울시 SNS</h3>
    .
    .
  </section>

<article>

🔍 각각의 section에는 h2태그로 제목 구성

제목이 눈에 보이지 않는 영역의 경우 IR기법을 사용한다.

<!--제목이 눈에 보이는 경우-->
<section class="sc-often">
  <h2 class="headline">자주 찾은 서비스</h2>
    .
    .
</section>

<!--제목이 눈에 보이지 않는 경우-->
<section class="sc-direct">
  <h2 class="blind">바로가기 링크</h2>
    .
    .
</section>

🔍 Class

클래스명에 통일성을 주어 직관적으로 구조를 파악할수있도록 구성

[sc- ] > [group- ] > [ -area]

📍 IS 기법(Image Sprite)

이미지 스프라이트란 ?

👉 여러 개의 백그라운드 이미지를 하나의 파일로 만든 후,
각 요소에 background-position 속성을 이용하여 이미지를 나타내는 방법.

이미지 스프라이트를 사용하면 좋은 점 !

✔️ 서버의 이미지 다운로드 요청 횟수가 줄어들어 사이트 로딩 빨라짐
✔️ 유지보수 시 하나의 이미지만 관리하면 된다.


📍 IR 기법 (Image Replacement) / WAI-ARIA

이미지를 볼 수 없는 사용자들에게 대체 텍스트를 제공하는 기법으로, 검색엔진과 스크린리더에는 노출이 되면서 시각적으로만 숨겨진다.

👉 각 섹션 제목이 시각적으로 보이지 않는 경우에도 IR기법 처리

<!-- 로고 예시 -->
<h1 class="logo"><a href="#"><span class="blind">서울특별시청</span></a></h1>

<!-- 제목 예시 -->
<section class="sc-visual">
 <h2 class="blind">주요뉴스 / 시민참여 이미지 슬라이드</h2>
 .
 .
</section>

👉 검색엔진 상 노출이 중요하지 않은 부분은 aria-label로 처리

<button class="prev" aria-label="이전슬라이드"></button>
<button class="play" aria-label="자동재생 시작"></button>
<button class="pause" aria-label="자동재생 일시정지"></button>
<button class="next" aria-label="다음슬라이드"></button>

👉 중복되는 정보는 따로 기재하지 않는다.

예시> figcaption이 이미지 내용을 설명하고 있기 때문에 alt값을 입력하지 않음

<div class="swiper-slide">
 <figure>
   <img src="./assets/images/news_2.jpg" alt>
   <figcaption>7월부터 지하철 10분 안에 다시 타면 무료…유의사항은?</figcaption>
 </figure>
</div>

⚠️ 주의 - <img alt=""> 형식은 입력되지 않은 정보로 인식하므로, <img alt>와 같은 형식으로 등호와 따옴표도 확실하게! 지워줘야한다.


마크업 시 a태그 안에 a태그를 사용하는 것은 문법상 옳지 않다.

위 사진처럼 각 영역과 개별 영역이 있는 경우, background 부분의 링크를 영역 전체로 잡고,
각 개별 링크는 따로 빼서 position:absolute로 영역을 잡아주어야 한다.

<section class="group-sns">
  <a href="#" class="link-bg">
    <h3 class="headline">서울시 SNS</h3>
  </a>
  <ul class="sns-area">
    <li><a class="instagram" href="#"><span class="blind">인스타그램</span></a></li>
    .
    .
  </ul>
</section>

📍 접근성, 편의성 Point

사용자의 접근성과 편의성 향상을 위해 사소한 디테일도 꼼꼼히 챙겨주었다 !

💎 본문 바로가기 버튼

스크린리더를 이용하는 사용자는 매 페이지 이동시마다 헤더부터 다시 읽어야하는 상황이 발생한다.
그리고 키보드 탭 버튼을 불필요하게 많이 눌러야 하는 애로사항도 생기는데,
본문 바로가기 버튼을 만들어 헤더의 내용을 건너뛸 수 있게 해주면 이런 불편함을 해소할 수 있다.

① a 태그를 이용해 버튼을 만들어주고, href 속성은 main 태그의 id값과 맞춰준다.

<div id="skipNav">
 <a href="#main">본문 내용 바로가기</a>
</div>
.
.
.
<main class="container" id="main">
.
.
</main>

② CSS를 이용해서 평소에는 가려져 있다가 tab버튼을 눌러 포커스가 잡히면 버튼이 보이도록 해준다.

#skipNav a{
   position: absolute;
   width: 100%;
   transform: translateY(-100%);
   background: #000;
   color: #fff;
   text-align: center;
   line-height: 50px;
}
#skipNav a:focus,
#skipNav a:active{
   transform:translate(0);
}

💎 태그의 나열 순서

영역 제목 옆에 전체보기 버튼이 있는 경우라도,
① 영역의 제목
② 영역의 내용들
③ 영역 전체보기 버튼
위 순서로 읽는 것이 자연스럽다.

마크업 시 위 순서를 고려하여 코딩하는 것이 사용자에게 더 편리함 !
더보기 버튼은 position:absolute를 이용하여 위치를 따로 잡아준다.

<section class="sc-board">
  <h2 class="headline">이벤트 신청</h2>
  <ul class="board-list">
  .
  .
  .
  </ul>
  <a href="#" class="link-more"><span class="blind">더보기</span></a>
</section>

💎 관련 사이트 영역

사용자의 편의성을 위해서 탭의 다른 영역을 클릭하면 닫히게 하고, 키보드 접근만으로도 탭을 쉽게 닫을 수 있도록 스크립트를 구성했다.

✨ 바깥 영역을 클릭하면 리스트 닫기

$(document).click(function(e){
   if($(`.sc-related`
   ).has(e.target).length === 0){
       $(`.site-area`).removeClass(`on`);
       $(`.open-btn`).removeClass(`on`);
   }
})

✨ 키보드 이벤트 편의성

// 첫번째 버튼에서 shift+tab 버튼 누를시 닫히기
$(`.related-item .site-list a:first-child`).keydown(function(e){
   if(e.keyCode === 9 && e.shiftKey){
       $(`.site-area`).removeClass(`on`);
       $(`.open-btn`).removeClass(`on`);
   }
})
// 마지막 버튼에서 tab 버튼 누를시 닫히기
$(`.related-item .site-list a:last-child`).keydown(function(e){
   if(e.keyCode === 9 && !e.shiftKey){
       $(`.site-area`).removeClass(`on`);
       $(`.open-btn`).removeClass(`on`);
   }
})

💎 맨위로 버튼

✨ 버튼 구성

버튼을 클릭하면 페이지 맨 위로 가는 버튼 구성

<button class="top-btn on">
 <span class="blind">맨위로</span>
</button>
$(`.top-btn`).click(function(){
 window.scrollTo({
   top:0,
   behavior:"smooth"
 });
})

✨ 현재 위치가 맨 위가 아닐때 나타나도록 함

position:fixed를 이용하여 위치를 고정시켜주고, bottom 값과 opacity, transition 속성을 활용하여 버튼이 떠오르면서 페이드인 / 아웃이 되도록 구성

.top-btn{
  position:fixed;
  .
  .
  bottom:0;
  opacity: 0;
  transition:0.3s;
}

.top-btn.on{
  bottom:98px;
  opacity: 1;
}
$(window).scroll(function(){
  curr = $(window).scrollTop();
    if(curr == 0){
      $(`.top-btn`).removeClass(`on`);
    }else{
      $(`.top-btn`).addClass(`on`);
    }
})

📍 Select-box와 window.open()을 활용한 언어 선택

💎 언어 선택 영역

GO버튼을 누르면 해당 언어를 지원하는 웹 사이트를 띄워주는 영역

① select 박스를 구성하고, option의 value값에 이동할 url을 넣어준다.

<div class="lang-area">
  <select name="language" id="languageList">
    <option value="https://english.seoul.go.kr/?SSid=101_01">ENGLISH</option>
    <option value="https://japanese.seoul.go.kr/?SSid=101_02">日本語</option>
    <option value="https://chinese.seoul.go.kr/?SSid=101_04">简体中文</option>
    <option value="https://tchinese.seoul.go.kr/?SSid=101_03">繁体中文</option>
    <option value="https://world.seoul.go.kr/">WorldWide</option>
  </select>
  <button class="lang-btn" id="langBtn">GO</button>
</div>

url함수에 value값을 담고, 버튼 클릭시 window.open(url)을 통해 새창을 띄우도록 했다.

$('#langBtn').click(function(){
    url = $('#languageList').val();
    window.open(url);
})

📍 swiper autoplay 재생, 일시정지 기능

swiper demo를 보니 기본으로 제공되는 autoplay 재생/정지 버튼이 없었다.

좀 더 찾아보니 쉽게 사용할 수 있는 메소드가 있어, 메소드를 사용해 재생/정지 버튼을 만들었다.
재생중인 상태에는 정지버튼이, 정지상태에는 재생버튼이 나타나도록 display값도 함께 바뀌게 했다.

const slider1_start = $(`.sc-visual .play`);
const slider1_pause = $(`.sc-visual .pause`);

slider1_start.click(function(){
  Slider1.autoplay.start();
  //재생버튼 숨김, 정지버튼 보임
  $(this).css("display","none");
  $(slider1_pause).css("display","block");
});

slider1_pause.click(function(){
  Slider1.autoplay.stop();
  //정지버튼 숨김, 재생버튼 보임
  $(this).css("display","none");
  $(slider1_start).css("display","block");
})

📍 swiper 따로 만든 탭 버튼과 연동

💎 버튼 구성

<div class="group-btn">
  <button class="news-btn on">주요뉴스</button>
  <button class="citizen-btn">시민참여</button>
</div>

✨ script ① / 버튼 클릭시 이동

Swiper에서 제공되는 slideToLoop 메소드를 이용하여 버튼 클릭시 각각 1번째, 5번째 슬라이드로 이동할 수 있도록 만들었다.

const news_btn = $(`.news-btn`);
const citizen_btn = $(`.citizen-btn`);

news_btn.click(function(){
    Slider1.slideToLoop(0);
});
citizen_btn.click(function(){
    Slider1.slideToLoop(4);
});

⚠️ 주의 - slideTo() 메소드도 있지만, Loop:true 상태에서는 제대로 동작하지 않기 때문에 slideToLoop()을 사용해야한다 !

✨ script ② / 활성화된 슬라이드에 따른 탭 클래스 컨트롤

1~4번째 슬라이드가 보일 때는 주요뉴스 탭, 5~14번째 슬라이드엔 시민참여 탭에 클래스가 붙을 수 있도록 만들어 활성화된 탭이라는 시각적인 정보를 얻을 수 있도록 했다.

인덱스 값은 역시 swiper에서 제공하는 메소드를 통해 얻을 수 있다.

Slider1.activeIndex;
Slider1.realIndex;

위 두가지 메소드가 있는데, activeIndex를 사용하면 Loop:true상태에서 정상적으로 동작하지 않고, realIndex는 loop상태에서도 제대로 동작한다.

const Slider1 = new Swiper(".sc-visual .swiper",{
  	.
  	.
    on:{ 
        // 인덱스값에 따라 탭 클래스 컨트롤
        slideChange: function(){
            if(Slider1.realIndex <= 3){
                $(`.news-btn`).addClass(`on`).siblings().removeClass(`on`);
            }else{
                $(`.citizen-btn`).addClass(`on`).siblings().removeClass(`on`);
            };
        }
    }
    
});

slideChange 이벤트와 조건문을 이용해 슬라이드가 바뀔때마다 인덱스값을 체크해 1~4번 슬라이드에서 주요뉴스 탭에 'on'클래스를 주고, 그렇지 않으면 시민참여 탭에 'on'클래스를 주도록 만들었다.


profile
배운것은 그날그날 잊지않고 기록하기

1개의 댓글

comment-user-thumbnail
2023년 7월 20일

항상 좋은 글 감사합니다.

답글 달기