mythinkg

ignocide's blog


  • 홈

  • 아카이브

  • 태그

시맨틱 마크업에 대하여

작성일 2017-09-03 | In web |
  • 개요
  • 뜻
  • 주의해야다고 느낀 것들..
  • 구조에서도..
  • 어쩔 수 없는 것도 있는 것 같다.

개요

어느날엔가 개발 블로그를 끄적거리다가 어느 회사의 채용페이지를 보았는데 눈에 띄는 부분이 있었다.

시맨틱한 마크업

분명 어디에서나 보았다. 어디에서는 시멘틱 웹, 시멘틱 검색, 시멘틱 마크업 등등의 이름으로 보였던것 같다.
누구나 신경 쓰는 부분일 텐데 정확한 정의가 떠오르지 않았다.

뜻

Semantic
뜻) 의미의, 의미론적인

이름이 모든걸 설명하고 있었다. 마크업을 작성할때 태그나 구조를 의미를 두고 작성하는 이것이 전부였다.
태그들의 이름들도 다 의미가 있고 그를 따르기만 하면 되는 것이다. 제목은 heading 뜻을 가진 <h*> 태그에 문단에 해당하는 부분은 paragraph(문단)의 뜻을 가진 태그인 <p>태그를 써서 작성한다. 마그업을 구성하는 이미지들에 있어서도 이러한 점들을 고려해야 한다. 내용에 사용되는 이미지로서의 이미지와 div(division)에 background를 사용한다거나 아이콘에 사용 할 수 있는데, 태그로 구분하여 문맥에서 사용하는 이미지로서의 구분을 해줘야 한다. 또한 alt속성 등을 확실하게 해주어 seo의 최적화와 더불어 문서에 의미를 부여해 줄 수 있다.
이러한 점에서 seo의 최적화에 아주 큰 기여를 하기도 한다.

주의해야다고 느낀 것들..

대표적이고 많이 쓰는 축에 속한다고 생각한것이 <b> 와 <strong> 태그와 같은 것들이다.
두개의 태그를 랜더링 후 보게되면, 대부분(아마도 모든)의 브라우져들은 둘다 font-weight: bold; 라는 스타일로 랜더링 해주기 때문에 무심코 차이를 두지 않고 사용 했을 태그들이다.
b는 bold, strong은 strong, 즉 진하게, 강하게 라는 각각의 뜻을 가지고 있다. 단순히 해석한 텍스트의 차이만큼의 차이가 있다. <b>는 텍스트를 진하게 표현하라는 의미에서의 진하게 표현된 것이고, <strong>은 강하게 표현하자는 의미에서 진하게 표현을 한 것이다. 크롤러들은 두가지의 차이를 두고 해석한다고 한다. 즉, 우리가 보는 시각적인 정보에서는 <b>는 강조되어 보이지만 크롤러는 일반 다른 문구와 차이를 두지 않을 것이라고 한다. (이또한 활용 할 수 있을 듯?)

mdn의 <b> 항목을 보면 다음과 같이 강조하여 설명한다.

<b> 요소와 <strong>, <em>,<mark> 요소들을 혼동하지 마세요. <strong> 요소는 중요한 글자를 나타내며, <em> 요소는 글자에 약간의 강조를 주며, <mark> 요소는 글자의 관련성을 나타냅니다. <b> 요소는 어떠한 특별한 시멘틱 정보를 전달하지 않습니다; 다른 맞는 요소가 없을떄 사용합니다.

열심히 설명했는데 mdn은 너무 쉽게 잘 설명했다..

이 글이 작성되고 있는 블로그 탬플릿인 jekyll은 markdown으로 작성되고 있다. 이 글들이 랜더링될 때 시맨틱 랜더링을 아주 잘 따르고 있다고 생각한다.

  • ### > <h3>
  • **강조** > <strong>
  • *약한강조* > <em>
  • ```코드블럭``` > <code>

등등 마크업으로 작성된 문서를 랜더링 된 것들과 비교하면 좋은 예제가 된다.

구조에서도..

일맥상통하게도 구조에서도 같은 의미를 가진다. <header>,<sidebar>,<section> 혹은 <article> 모던웹 구조라고 알려진 이러한 태그들 또한 시맨틱 마크업이라는 의미 안에 있다.
이부분에 대해서는 각각의 태그들의 이름이 너무나도 직관적인 듯 하다.
고로 설명도 pass

어쩔 수 없는 것도 있는 것 같다.

image를 뜻하는 img 태그 등, 이미지가 아니더라도 단일적인 마크업과 다르게 좀 더 기능적으로 사용할 수 있는 picture,figure 태그도 있다.
문서 설명 picture,figure들를 읽어보니 문서를 구조화하고 의미를 부여하는데 용이한 기능적인 것들이 포함 되어 있다. 하지만 호환성의 문제(물론 주로 internet eXXXX)때문에 일일이 신경 써주기 힘든 부분도 있기 때문에 현실과 타협하게 되면 사용을 안하게 되는 부분이 큰 듯 하다.

더 읽어보기 »

[hackerrank solution] Kangaroo

작성일 2017-08-21 | In hackerrank |

solution

function kangaroo (x1, v1, x2, v2) {
    // Complete this function
  var aws = (x2 - x1) / (v1 - v2)
  if (aws > 0 && aws == parseInt(aws)) {
    return 'YES'
  }
  return 'NO'
}

function main () {
  var x1_temp = readLine().split(' ')
  var x1 = parseInt(x1_temp[0])
  var v1 = parseInt(x1_temp[1])
  var x2 = parseInt(x1_temp[2])
  var v2 = parseInt(x1_temp[3])
  var result = kangaroo(x1, v1, x2, v2)
  process.stdout.write('' + result + '\n')
}
더 읽어보기 »

[hackerrank solution] Time Conversion

작성일 2017-08-21 | In hackerrank |

solution

function timeConversion (s) {
    // Complete this function
  var time = s.slice(0, 8)
  var word = s.slice(8, 10)

  var arr = time.split(':')

  if (word == 'AM') {
    if (arr[0] == '12') {
      arr[0] = '00'
    }

    return arr.join(':')
  }

  if (arr[0] !== '12') {
    arr[0] = parseInt(arr[0]) + 12
  }

  return arr.join(':')
}

function main () {
  var s = readLine()
  var result = timeConversion(s)
  process.stdout.write('' + result + '\n')
}
더 읽어보기 »

[hackerrank solution] Diagonal Difference

작성일 2017-08-03 | In hackerrank |

solution

function main () {
  var n = parseInt(readLine())
  var a = []
  for (a_i = 0; a_i < n; a_i++) {
    a[a_i] = readLine().split(' ')
    a[a_i] = a[a_i].map(Number)
  }

  var pri = a.reduce(function (sum, v, k) {
    return sum + v[k]
  }, 0)
  var sec = a.reduce(function (sum, v, k) {
    return sum + v[n - 1 - k]
  }, 0)

  var result = Math.abs(pri - sec)

  console.log(result)
}
더 읽어보기 »

[hackerrank solution] Mini-Max Sum

작성일 2017-08-03 | In hackerrank |

solution

function main () {
  arr = readLine().split(' ')
  arr = arr.map(Number)
  arr.sort()

  var sum = arr.reduce(function (sum, num) {
    return sum + num
  }, 0)

  console.log((sum - arr[arr.length - 1]) + ' ' + (sum - arr[0]))
}
더 읽어보기 »

[hackerrank solution] Staircase

작성일 2017-08-03 | In hackerrank |

solution

function main () {
  var n = parseInt(readLine())

  var space = ' '
  var symbol = '#'

  var result = []
  for (var i = 0; i < n; i++) {
    result.push(space.repeat(n - i - 1) + symbol.repeat(i + 1))
  }

  console.log(result.join('\n'))
}
더 읽어보기 »

[nodejs] sailsjs로 웹서비스 만들기 개요 + 환경설정

작성일 2017-07-30 | In nodejs |

배경

nodejs를 이용한 웹 프로그래밍을 시작하며 가장 먼저 접하게 되는 것은 아마도 express.js 일것입니다.
여러 사람들이 expressjs프레임워크의 장점으로 뽑는다면 높은 자유도, 즉 가장 기본적으로 세팅되어 있는 점을 뽑을 듯합니다.

반대로 sailsjs는 웹서비스를 준비하며 필요한 요구사항들을 여럿 담고 있습니다.

  • waterline 이라는 자체적인 database orm부터
  • 다국어를 위한 i18n, task관리인 grunt,
  • restful test를 하는 supertest,
  • 제가 잘 사용하지 못하지만 빠르고 정교한 개발을 가능케하는 blueprint를 활용하고,
  • 많이 사용하는 유틸 라이브러리인 async, lodash 등도 사용합니다.

sailsjs프로젝트의 structure 또한 좀더 정규화 되어 있어 구조에 대한 고민이나 추가적인 설정 또한 해준다는 장점이 있습니다.
실제로 업무에서 구현되어었던 express에서 추가로 개발한것들에 대하여 대신하여 편리하고 좀 더 빠른 개발을 가능하게 했습니다.
sailsjs app structure

config폴더를 보시면 전처리를 위한 bootstrap, 환경 분리를 위한 env/(development,production), db커넥션을 위한 connectionjs, log처리 등 모든 것들이 준비되어있습니다.

위와 같은 이유로 친구가 필요로 하는 작은 서비스를 빠르게 개발 하기 위해 sailsjs를 이용하여 개발하였습니다.

시작

여느 프로젝트와 같이 설치로 시작합니다

  1. sails를 설치를 하고

    npm install -g sails

  2. sails프로젝트를 만들고

    sails new projectname

  3. 시작합니다

    cd projectname
    sails lift

nodemon 연동

grunt config 설정에 대한 세팅들은 tasks/config에 설정을 합니다.

우선 grunt-nodemon을 설치해주고

npm install –save-dev grunt-nodemon

task에 대한 설정을 해줍니다.

//tasks/config/nodemon.js
module.exports = function (grunt) {
  grunt.config.set('nodemon', {
    dev: {
      script: '../../app.js',
      options: {
        args: ['dev', "--models.migrate='drop'"],
        nodeArgs: ['--debug'],
        callback: function (nodemon) {
          nodemon.on('log', function (event) {
            console.log(event.colour)
          })
        },
        cwd: __dirname,
        ignore: ['node_modules/**'],
        ext: 'js',
        watch: ['../../api', '../../config'],
        delay: 1000,
        legacyWatch: true
      }
    },
    exec: {
      options: {
        exec: 'less'
      }
    }
  })

  grunt.loadNpmTasks('grunt-nodemon')
}

이후 tasks/register에 tssk를 작성합니다

//tasks/register/dev
module.exports = function (grunt) {
  grunt.registerTask('dev', ['nodemon'])
}

이제

grunt dev

를 실행하여 nodemon을 적용합니다.

config설정하기

기본적으로 config디렉토리 아래 작성되어 있는 파일들이 공통으로 사용하는 설정을 하게 되고,
env 폴더 아래 development, production으로 나누어 환경을 분리 할 수 있습니다.

예로 저 같은 경우는 로컬에서 개발할 경우 데이터베이스를 사용하기 힘든 환경이기 때문에

  • 로컬에서는 sails-disk라는 로컬파일로 저장하여 db를 사용하게 하는 것을 사용할것이고
  • 실사용에서는 mysql를 사용할 것이라면

connections.js에 두가지 모두 선언을 해주고

//config/connections.js
  localDiskDb: {
    adapter: 'sails-disk'
  },
  mysqlServer: {
    adapter: 'sails-mysql',
    host: 'YOUR_MYSQL_SERVER_HOSTNAME_OR_IP_ADDRESS',
    user: 'YOUR_MYSQL_USER', //optional
    password: 'YOUR_MYSQL_PASSWORD', //optional
    database: 'YOUR_MYSQL_DB' //optional
  }

각각의 환경에 대해서

//config/env/development.js
models: {
  connection: 'localDiskDb'
},
log: {
  level: 'info'
}
//config/env/production.js
models: {
  connection: 'mysqlServer'
},
log: {
  level: 'silent'
}

로 작성하게 된다면, db에 대한 환경을 분리하고 로그에 대해서는 실서버에서는 안나오게 되게끔 분리됩니다.

더 읽어보기 »

[hackerrank solution] Grading Students

작성일 2017-07-30 | In hackerrank |

solution


function solve (grades) {
    // Complete this function
  var n = grades.length
  var board = []
  for (var i = 0; i < n; i++) {
    var tmp = grades[i]
    var grade = rescore(grades[i])
    grade >= 40 ? board.push(grade) : board.push(tmp)
  }

  return board
}

function rescore (grade) {
  var i = grade % 10
  var a = i - 5 < 0 ? 5 : 10
  if (a - i < 3) {
    grade = grade - i + a
  }

  return grade
}

function main () {
  var n = parseInt(readLine())
  var grades = []
  for (var grades_i = 0; grades_i < n; grades_i++) {
    grades[grades_i] = parseInt(readLine())
  }
  var result = solve(grades)
  console.log(result.join('\n'))
}
더 읽어보기 »

jekyll에서의 SEO 적용, blockquote,toc 디자인 적용

작성일 2017-07-22 | In web |
  • SEO 적용
    • 사용방법
      • Gemfile
      • config.yml
      • default.html
      • sitemap
  • blockquote,toc 디자인 적용
    • 사용방법

SEO 적용

페이지를 만든다고 해서 무작정 웹페이지를 만들면서 seo적용은 필수 불가결 하다고 볼수 있다.
search engine optimization, 뜻 그대로 검색 엔진 최적화 작업이다.

html 마크업에서의 헤더에 정해진 태그를 읽고 그 페이지의 속성, 성격, 정보를 얻고 그를 토대로 검색시 결과에 반영해 주기 때문에 필수 불가결 하다고 한것이다.
그 외적으로도 twitter, facebook 등에서 자체적으로

jekyll를 이용한 정적블로그가 많이 늘어난 만큼 jekyll seo 모듈이 있다. 이를 활용해서 쉽게 적용할수 있다.

사용방법

Gemfile

...
gem 'jekyll-seo-tag'
gem 'jekyll-sitemap'

bundle install

config.yml

gems:
  - jekyll-seo-tag
  - jekyll-sitemap

default.html

<head>
...
{% seo %}
...
</head>

이렇게 적용을 한 뒤 소스를 보게 되면

title, og:title, og:locale, description, ….등등의 여럿 seo에 도움되는 태그들을 작성한 게시물을 기준으로 생성된 것을 볼수 있습니다.

sitemap

또한 jekyll-sitemap을 설치 하였다면, sitemap.xml이 생성된 것을 볼 수 있습니다.

참고페이지 - sitemap
참고페이지 - robots.txt
크롤러는 먼저 robots.txt에 접근을 하게 되고 robots.txt를 해독하여 사이트에서의 접근과 비접근 부분을 구분하여 활동하게 됩니다. 이는 강제적인게 아니기 때문에 크롤러가 무시할 수 있습니다. 떄문에 신뢰되는 검색엔진에서는 검색의 결과를 숨길수 있는 효과를 가져올 수 있지만 페이지를 숨기기 위한 목적으로 사용하기에는 부적절 할 수 있습니다.

sitemap.xml의 역활은 google이 사이트를 크롤링 할 경우, 해당 크롤러가 사이트의 크롤링할 페이지를 가이드 해주는 것이라고 이해하면 됩니다. 크롤러는 sitemap파일을 보고 각각 페이지에 들어가여 색인작업을 하게 됩니다.
참고 페이지에서 가이드를 해주는 내용입니다만, 모듈을 이용하게 되면, sitemap.xml의 생성과 더불어 robots.txt를 아래와 같이 작성해줍니다.

Sitemap: ...host.../sitemap.xml

이로서 jekyll기반의 블로그가 검색엔진에 노출될 수 있습니다.

sitemap.xml등록에 대해서는 구글 웹마스터 도구를 이용해서 수동으로 할 수도 있습니다.

blockquote,toc 디자인 적용

사용방법

* TOC
{:toc}

> blockquote
더 읽어보기 »

[android] RecyclerView 에서의 setOnItemClickListener (흉내내기)

작성일 2016-07-26 | In android |

개요

  • 개요
  • 흉내내보자

RecyclerView는 ListView에서의 비효율적인 리스트 아이템 생성과 같은 점을 극복하고자 좀 더 유연하고 좀 더 효율적으로 사용되기 위해 만들어졌다고 합니다.
RecyclerView이 만들기 이전에도 ListView에서 ViewHolder개념을 응용? 도입? 해서 효율적으로 사용하였지만 RecyclerView에선 강제적으로 사용하게 되었다고 합니다.

하지만 Listview에서 setOnItemClickListener 와 같은 기능을 지원하지 않습니다. addOnItemTouchListener와 같은 기능이 있지만 listview에서와 같이 직접적으로 position에 접근을 할 수 없고 여러 이슈가 있다고 합니다.

흉내내보자

RecyclerView에서 onBindViewHolder해줄 때에 각각의 아이템에 OnClickListener를 등록해 주는 것으로 접근했습니다.
코드는 핵심적인 부분만 중략해서 작성했습니다.

public class RecyclerAdapter extends RecyclerView.Adapter{
    private ArrayList strings;

    //아이템 클릭시 실행 함수
    private ItemClick itemClick;
    public interface ItemClick {
        public void onClick(View view,int position);
    }

    //아이템 클릭시 실행 함수 등록 함수
    public void setItemClick(ItemClick itemClick) {
        this.itemClick = itemClick;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder{
        View view;
        public ViewHolder(View view) {
            super(view);
            this.view = view;
        }
    }

    @Override
    public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                         int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_items, null);
        ViewHolder vh = new ViewHolder(v);

        return vh;
    }


    //중략 ...................

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        final int Position = position;
        //중략 ...................
        holder.view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(itemClick != null){
                    itemClick.onClick(v, Position);
                }
            }
        });
    }
    //중략 ...................

}

adapter 부분에서 이와 같은 방식으로 작성하였습니다.
이후에 recycler 호출부분에서 아래와 같이 작성한다면, listview에서 setOnItemClickListener 와같이 작동할 수 있습니다.

        mAdapter.setItemClick(new RecyclerAdapter.ItemClick() {
            @Override
            public void onClick(View view, int position) {
                //클릭시 실행될 함수 작성
            }
        });

코드가 길어져 상당히 중략을 하였습니다.

제가 작성한 예제 코드는
안드로이드 스터디 프로젝트 https://github.com/ignocide/android_study/에 공유하고 있습니다.

더 읽어보기 »
1 2 3
ignocide

ignocide

web developr

22 포스트
8 카테고리
11 태그
RSS
© 2018 ignocide
Powered by Jekyll
Theme - NexT.Muse