#ccc 50개의 스레드 ✕ 해제
이온디
이온디 1개월 전
시리즈 ← 이전 편 바이브코딩의 위험성 ③ — 사람은 잊지만 코드는 잊지 않는다 사고는 위험한 자유의 부산물이 아니라, 안전망 부재의 결과다. — 시리즈 3편 / 회고와 안전망 설계 바이브코딩의 양면성 "바이브코딩(vibe coding)"이라는 말이 유행어가 되었다. AI에게 분위기와 의도만 던져주면 코드가 술술 나오는, 자연어로 개발하는 시대. 코드 한 줄 모르는 사람도 며칠이면 자기 사이트를 만든다. 우리는 더 이상 if-else나 for-loop을 외울 필요가 없다. 그 자리에 들… 시리즈 ← 이전 편 바이브코딩의 위험성 ③ — 사람은 잊지만 코드는 잊지 않는다 사고는 위험한 자유의 부산물이 아니라, 안전망 부재의 결과다. — 시리즈 3편 / 회고와 안전망 설계 바이브코딩의 양면성 "바이브코딩(vibe coding)"이라는 말이 유행어가 되었다. AI에게 분위기와 의도만 던져주면 코드가 술술 나오는, 자연어로 개발하는 시대. 코드 한 줄 모르는 사람도 며칠이면 자기 사이트를 만든다. 우리는 더 이상 if-else나 for-loop을 외울 필요가 없다. 그 자리에 들어선 건 기획·아이디어·도메인 이해다. 좋은 시대가 온 게 맞다. 그런데 좋은 시대는 새로운 종류의 사고도 함께 들고 온다. 이번 사고가 바로 그 예시였다. 평범한 모델 파일 하나를 추가하면서, alembic env.py에 import 한 줄을 추가하지 않았다. 그것 하나로 1091줄짜리 폭탄 마이그레이션이 만들어졌다. 사람은 한 줄을 잊었을 뿐인데, 자동화 도구는 그 망각을 30개 테이블의 DROP 문으로 증폭시켰다. AI 도구든 alembic 같은 자동화 스크립트든, 자동화는 인간의 실수를 대신 봐주지 않는다. 오히려 증폭시킨다. "조심하자"는 답이 아니다 가장 흔한 사후 대처는 "다음부터 조심하자"다. 그리고 가장 자주 실패하는 대처도 "다음부터 조심하자"다. env.py에 import를 빠뜨렸을 때 누구도 일부러 그런 게 아니다. 새 모델 추가하느라 정신없는 와중에, env.py라는 한참 떨어진 파일을 동시에 떠올리는 게 어려웠을 뿐이다. 인간은 작업 중에 컨텍스트를 좁힌다. 그게 결함이 아니라 본성이다. 그래서 답은 사람의 주의력에 의존하지 않는 구조다. 인간이 잊을 수 있는 자리를 자동화로 채우는 것. 이번 사고에서 우리가 한 일은 사실 그 한 가지였다. for _info in pkgutil.iter_modules(_models_pkg.__path__): if _info.name.startswith("_") or _info.name == "base": continue importlib.import_module(f"app.models.{_info.name}") 이 6줄이 의미하는 바는 단순하다. 누구도 더 이상 import를 까먹을 수 없다. 모델 파일이 디렉터리에 있으면 자동으로 잡힌다. 잊을 수 있는 자리 자체를 없앤 것이다. 3중 방어 — 사고를 몇 단계에서 막을 것인가 좋은 시스템은 한 군데에서 막는 게 아니라, 여러 군데에서 같은 사고를 잡는다. 첫 번째 방어선이 뚫려도 두 번째가 있고, 두 번째가 뚫려도 세 번째가 있어야 한다. 이번 일을 계기로 마이그레이션 사고에 대한 3중 방어를 설계했다. 1차 방어 — 모델 메타데이터 자동 등록 방금 본 그것. 누락 자체가 일어나지 않도록 구조를 바꾼다. 2차 방어 — Pre-commit gate 혹시라도 1차가 뚫려서 위험한 마이그레이션이 만들어졌다면, commit 단계에서 차단한다. 신규 마이그레이션 파일에 drop_table이나 drop_index가 일정 개수 이상이면 git이 commit을 거부하도록 한다. # .git/hooks/pre-commit NEW_MIGRATIONS=$(git diff --cached --name-only --diff-filter=A | grep '^alembic/versions/.*\.py$') for f in $NEW_MIGRATIONS; do DROPS=$(grep -cE "drop_table|drop_index" "$f") if [ "$DROPS" -ge 3 ]; then echo "❌ $f 에 drop 작업이 $DROPS 건 있습니다. 의도된 변경인지 확인하고 --no-verify로 우회하세요." exit 1 fi done 3차 방어 — Pre-deploy gate 다른 사람이 다른 환경에서 commit한 게 production까지 흘러왔다고 해도, 배포 직전에 한 번 더 막는다. 배포 스크립트(server.sh)에서 alembic upgrade를 돌리기 전에 dry-run을 먼저 시킨다. SQL=$(alembic upgrade --sql current:head) if echo "$SQL" | grep -qE "DROP TABLE|TRUNCATE"; then echo "❌ 배포 중단 — 위험한 SQL 감지" exit 1 fi alembic upgrade head 이 세 단계 중 어느 한 곳도 사람의 주의력에 의존하지 않는다. 자고 있어도, 출근길에 정신없어도, 이미 쳤던 명령을 의식 없이 다시 쳐도 — 안 터진다. 백업 자동화와 binlog 이번 사고에서 가장 손발이 묶였던 순간은 binlog가 OFF라는 걸 확인했을 때였다. 점-시간 복구라는, 데이터베이스의 마지막 안전망이 처음부터 없었던 것이다. binlog는 MySQL의 변경 이력을 별도 파일에 기록하는 기능이다. 이게 켜져 있으면 "어제 14:50:59 시점으로 복구해줘"가 가능하다. 꺼져 있으면 그냥 마지막 백업 시점까지밖에 못 돌아간다. 켜는 건 한 줄이다. /etc/mysql/mysql.conf.d/mysqld.cnf에: log_bin = /var/log/mysql/mysql-bin.log expire_logs_days = 7 expire_logs_days = 7이 중요하다. 안 적으면 binlog가 무한히 쌓여서 디스크가 꽉 찬다. 7일이면 보통의 사고 추적엔 충분하고, 디스크 부담도 적다. 그리고 binlog가 있어도 풀 백업은 따로 필요하다. 복구는 "풀 백업 + 그 이후의 binlog" 조합으로 이뤄지기 때문이다. 풀 백업은 cron으로 자동화한다. # 매일 새벽 3시 백업 0 3 * * * /home/.../scripts/db_backup.sh >> /home/.../private/backup.log 2>&1 # 매일 새벽 3시 30분, 14일 이전 백업 자동 삭제 30 3 * * * find /home/.../private/ -name '*-*.sql.gz' -mtime +14 -delete 이 두 줄이면 매일 새벽 백업이 만들어지고, 14일 이전 백업이 자동 정리된다. 한 번 설정하면 잊고 살아도 된다. 디테일이 안전을 만든다 이번 작업에서 사소해 보이지만 중요했던 디테일들을 적어둔다. set -euo pipefail — 백업 스크립트 첫 줄. 한 줄이라도 실패하면 즉시 멈춘다. 이게 없으면 mysqldump가 중간에 실패해도 gzip은 부분 데이터를 압축하면서 "성공" 종료 코드를 반환할 수 있다. 부분 백업은 백업이 아니다. 이 한 줄이 그 차이를 만든다. --single-transaction — InnoDB에서 락 없이 일관된 스냅샷을 떠준다. production 트래픽에 영향을 거의 안 주면서 데이터 일관성을 보장. --routines --triggers --events — 이걸 빼먹으면 트리거·뷰·저장 프로시저가 백업에 안 들어간다. 복원 후에 알 수 없는 동작이 사라진 채로 운영되는 상황이 생긴다. OUT_DIR="$HOME/web/.../private" — 절대 경로 대신 $HOME을 쓰면 다른 서버나 다른 계정에서도 그대로 동작한다. 그리고 백업 위치가 웹에서 노출되지 않는 디렉터리인지 반드시 확인한다. chmod +x를 로컬에서 미리 — rsync는 파일 권한을 그대로 복사한다. 로컬에서 +x를 안 했으면 production에서 또 chmod를 해야 한다. 작은 일이지만, 매번 잊는 종류의 일이다. ~/.ssh/config 별칭 등록 — 매번 풀 도메인 치는 대신 ssh mars 한 단어로 접속. 작은 편의지만, 이런 작은 마찰이 쌓이면 보안 작업도 귀찮아진다. (NEUTRALIZED) 같은 명시적 표시 — 비운 마이그레이션의 docstring 첫 줄에 이렇게 넣어두면, dry-run 출력에도 그대로 떠서 "지금 처리되는 게 무력화된 파일이 맞다"는 즉각적인 시각 확인이 된다. 6개월 뒤의 자기 자신을 위한 친절. 이런 디테일들은 하나하나는 사소하지만, 사고 한가운데에서 마음을 가라앉히는 건 결국 이런 작은 확실성들이다. 다 끝난 뒤에 남은 것 이번 사고로 잃은 데이터는 단순 로그 정도였고 다시 채울 수 있는 수준이었다. 하지만 만약 폭탄이 발견되지 않은 채 다음 배포 때 터졌다면? 그땐 30개 테이블이 진짜로 사라졌을 것이다. 그중에는 단순 로그만 있는 게 아니었다 — 고객 관리, 마케팅 캠페인, 뉴스레터 구독자, 게시판 SEO 설정 같은, 다시 채우려면 외부 자료 없이는 불가능한 데이터들이 섞여 있었다. 운이 좋았다. 그런데 운이 좋았다는 건 시스템이 안전했다는 게 아니라, 단지 이번엔 누군가 일찍 알아챘다는 뜻일 뿐이다. 다음 사고는 늦게 발견될 수도 있다. 자동화의 시대에는, 사고가 너무 빨리 너무 광범위하게 퍼진다. 알아챈 시점엔 이미 늦은 경우가 많다. 그래서 우리는 안전망을 코드에 박아둔다. env.py의 자동 import, pre-commit hook, pre-deploy gate, 매일 백업, binlog. 사람은 잊지만 코드는 잊지 않으니까. 바이브코딩이 위험한 게 아니다. 안전망 없는 자동화가 위험한 것이다. 자연어로 개발하는 시대에, 우리가 새로 배워야 할 건 아마 코드 그 자체가 아니라 — 자동화의 사각지대를 미리 메우는 감각일 것이다. 당신의 바이브코딩은 안전한가요? AI에게 코드를 맡겼다면, 안전망도 같이 맡겼는지 한 번 점검해보세요. 7가지 항목으로 끝나는 무료 자가 진단. 바이브코딩 안전 체크리스트 받기 → 시리즈 끝. 다음에는 더 안전한 코드로 만나길.
이온디
이온디 1개월 전
시리즈 ← 이전 편 · 다음 편 → 바이브코딩의 위험성 ② — 범인은 autogenerate였다 1091줄짜리 시한폭탄을 만든 건 사람이 아니라 자동화 도구 자신이었다. — 시리즈 2편 / 진짜 원인 발견과 해체 작업 Alembic autogenerate가 그러는 이유 Alembic은 SQLAlchemy 기반 프로젝트의 마이그레이션 도구다. alembic revision --autogenerate라는 명령을 치면, 현재 모델 정의(SQLAlchemy Base.metadata)와 실제… 시리즈 ← 이전 편 · 다음 편 → 바이브코딩의 위험성 ② — 범인은 autogenerate였다 1091줄짜리 시한폭탄을 만든 건 사람이 아니라 자동화 도구 자신이었다. — 시리즈 2편 / 진짜 원인 발견과 해체 작업 Alembic autogenerate가 그러는 이유 Alembic은 SQLAlchemy 기반 프로젝트의 마이그레이션 도구다. alembic revision --autogenerate라는 명령을 치면, 현재 모델 정의(SQLAlchemy Base.metadata)와 실제 DB 스키마를 비교해서 그 차이를 마이그레이션 파일로 자동 생성해준다. 컬럼 추가, 인덱스 추가, 테이블 추가 같은 변경을 사람이 손으로 SQL을 적지 않아도 되게 해주는 편리한 도구다. 문제는 이 비교의 방향성이다. Alembic은 "모델에는 있는데 DB에 없는 것"을 추가 작업으로 인식하고, 동시에 "DB에는 있는데 모델에 없는 것"을 삭제 작업으로 인식한다. 후자가 함정이다. Base.metadata에 어떤 모델이 등록되지 않은 상태에서 autogenerate를 돌리면, 실제로는 코드 어딘가에 살아 있는 모델이라도 alembic 입장에선 "사라진 테이블"이 된다. 그러면 친절하게 op.drop_table('...')을 자동으로 만들어준다. alembic/env.py를 열어봤다. from app.models import document, member, comment, file, module, site_config, site from app.models import hosting_site, hosting_subscription, project, inquiry, member_profile from app.models import sale_product, order, revenue, settlement from app.models import project_issue, kakao_chat, project_billing, project_file, project_comment 손으로 적은 import가 21개. 그런데 app/models/ 디렉터리에는 모델 파일이 57개. 빠진 36개의 정체: audit_log.py bank_transaction.py blog_post.py client.py hosting_setup_log.py marketing.py notification_log.py spam.py wiki.py analytics_report.py module_group.py newsletter.py ... 폭탄 마이그레이션이 DROP하려던 그 테이블들이, 정확히 env.py에서 import 누락된 모델 파일들과 일치했다. .title { font-size: 22px; font-weight: 700; fill: #111; } .subtitle { font-size: 14px; fill: #666; } .label { font-size: 14px; fill: #222; font-weight: 500; } .num { font-size: 28px; font-weight: 700; } .small { font-size: 12px; fill: #555; } .bad { fill: #c83737; } .good { fill: #1f7a3f; } .arrow { stroke: #777; stroke-width: 1.6; fill: none; } .arrow-bad { stroke: #c83737; stroke-width: 1.8; fill: none; stroke-dasharray: 4 3; } .arrow-good { stroke: #1f7a3f; stroke-width: 1.8; fill: none; } .box { stroke: #ccc; stroke-width: 1; fill: #fff; rx: 8; } .box-bad { stroke: #e0a8a8; stroke-width: 1; fill: #fff5f5; rx: 8; } .box-good { stroke: #a8d4b8; stroke-width: 1; fill: #f3faf5; rx: 8; } .row-label{ font-size: 13px; fill: #888; font-weight: 600; letter-spacing: 1px; } env.py의 침묵하는 누락 손으로 적은 import 21개 vs 디렉터리에 실재하는 모델 57개 BEFORE — 사고 시점 app/models/ 57 실재 모델 파일 env.py 21 손으로 적은 import Base.metadata 21 등록된 테이블 autogenerate −36 DROP TABLE 자동 생성 metadata에 없는 36개 모델은 alembic 입장에서 "사라진 테이블"로 보임 → 1091줄짜리 폭탄 마이그레이션 파일이 자동 생성됨 AFTER — env.py 자동 import 적용 app/models/ 57 실재 모델 파일 env.py auto pkgutil.iter_modules Base.metadata 78 전체 자동 등록 autogenerate 0 false-DROP 차단 새 모델 파일을 추가해도 env.py에 손대지 않아도 됨 — 잊을 수 있는 자리 자체가 사라짐 env.py에 손으로 적은 21개 import vs 디렉터리에 실재하는 57개 모델 — 그 격차가 폭탄을 만든다. 원인 확정. 사람의 게으름이 아니라, 시스템 설계의 문제였다. 새 모델을 추가할 때마다 env.py에 한 줄을 손으로 더 적어야 하는 구조 자체가, 언젠가 누락이 생길 시한폭탄이었다. 백업이 가장 먼저 원인을 알았다고 해서 바로 수정 작업에 들어가면 안 된다. binlog가 꺼진 상태에서, 잘못된 한 줄이 더 큰 사고를 만들 수도 있다. 이번 작업의 모든 안전성은 현재 시점 백업 한 장에 달려 있었다. 백업 스크립트를 짜기 전에 한 가지 고려사항이 있었다 — eondcms는 트래픽이 꽤 있는 사이트라 카운터·통계·API 호출 로그 테이블 3개가 매우 크다. 이걸 그대로 풀 덤프하면 시간도 오래 걸리고 용량도 부담스럽다. 이 3개는 데이터는 빼고 스키마만 보존하기로 했다. 복원 후에도 빈 테이블 껍데기는 만들어져야 ORM이 INSERT를 시도할 때 에러가 나지 않으니까. 핵심 패턴은 단순하다. mysqldump를 두 번 호출해서 stdout을 이어붙인 뒤 한 번에 gzip으로 압축한다. 결과물은 단일 파일. { mysqldump --single-transaction --routines --triggers --events \ --default-character-set=utf8mb4 --hex-blob \ --ignore-table=$DB.xe_counter_log \ --ignore-table=$DB.xe_api_call_logs \ --ignore-table=$DB.xe_stats_log \ "$DB" mysqldump --no-data --default-character-set=utf8mb4 \ "$DB" xe_counter_log xe_api_call_logs xe_stats_log } | gzip > "$OUT_FILE" 스크립트 첫 줄에 set -euo pipefail을 박아두는 게 중요하다. 한 줄이라도 실패하면 즉시 멈추도록. 백업이 도중에 깨졌는데 "성공"이라고 착각하는 사고가 가장 흔하니까. 결과물은 압축 후 37MB. 압축 전 원본 기준 약 300~450MB 추정. 이걸 웹에서 절대 보이지 않는 디렉터리에 저장하는 것도 중요했다. 백업 파일에는 비밀번호 해시·세션 토큰·이메일 같은 민감정보가 그대로 들어 있어서, "private"이라는 이름의 디렉터리에 둔다고 해도 실제로 웹서버 설정이 막아주지 않으면 누구나 다운로드 가능하다. env.py 자동화 — 사람의 주의력에 의존하지 않기 이제 진짜 수정. env.py를 손으로 import 목록을 유지하는 방식이 사고의 근본 원인이라면, 답은 자동 import다. 새 모델 파일이 추가될 때마다 자동으로 등록되도록 바꾸면, 누구도 손으로 적는 걸 까먹을 수 없다. import importlib import pkgutil import app.models as _models_pkg for _info in pkgutil.iter_modules(_models_pkg.__path__): if _info.name.startswith("_") or _info.name == "base": continue importlib.import_module(f"app.models.{_info.name}") pkgutil.iter_modules는 패키지 안의 모든 모듈을 순회해주는 표준 라이브러리. 베이스 모듈만 제외하고 전부 import한다. 검증 결과 55개 모듈이 자동 로드되어 78개 테이블이 metadata에 등록되었다. 이전 21개 import로 잡지 못했던 36개 모델이 이번에 모두 합류했다. 이 변경 한 줄이 의미하는 바는 명확하다. 앞으로 누가 새 모델 파일을 만들어도 env.py를 손대지 않아도 된다. autogenerate가 잘못된 DROP을 만들 가능성이 구조적으로 차단된다. 폭탄 마이그레이션 무력화 남은 일은 1091줄짜리 폭탄을 어떻게 처리할 것인가였다. 선택지는 두 개: A. 파일 자체를 삭제 — 깔끔해 보이지만, alembic 입장에선 chain의 한 노드가 사라지는 것이라 후속 마이그레이션이 깨진다. B. 내용만 비우기 — revision과 down_revision 필드는 그대로 두고, upgrade()와 downgrade() 함수 본문을 pass로 교체한다. chain은 그대로, 동작만 무력화. B를 골랐다. 사고 경위와 신원 보존이 되는 주석을 헤더에 적어두고: """add_random_order_to_board_config (NEUTRALIZED) ⚠️ 이 마이그레이션은 의도적으로 비워졌습니다 (2026-04-28). 사고 경위: alembic env.py가 app/models/ 아래 일부 모델만 import하던 상태에서 --autogenerate 가 실행되어, 누락된 36개 모델이 "사라진 테이블"로 인식 → 30+ 테이블 DROP을 자동 생성한 1091줄짜리 폭탄 마이그레이션이 됨. """ def upgrade() -> None: pass def downgrade() -> None: pass 이 주석은 미래의 누군가가 — 6개월 뒤의 자기 자신을 포함해서 — "왜 이 파일이 비어있지?" 라고 물었을 때, 짧은 단서가 되어줄 것이다. 코드 자체가 자기 역사를 설명할 수 있어야 한다. 안전한 적용 절차 수정한 파일들을 production에 보내기 전, 한 가지 더 확인할 게 있었다. dry-run. alembic upgrade --sql은 offline 모드로 동작해서, DB에 연결조차 하지 않고 실행될 SQL을 텍스트로만 출력한다. 진짜 실행 전에 무엇이 production에서 일어날지 미리 보여주는 안전 기능이다. alembic upgrade --sql c3d4e5f6a1b2:head | grep -E "DROP TABLE|TRUNCATE" \ && echo "❌ 아직 위험" \ || echo "✅ no destructive ops" grep이 무언가 잡히면 → 위험. 아무것도 안 나오면 → 안전. 아주 단순하지만 마음 편한 검증이다. 처음 production에서 dry-run을 돌렸을 때는 DROP 문이 좌라락 출력되었다. 잠깐 가슴이 철렁했지만, 곧 이유를 알았다 — 우리가 로컬에서 비운 두 파일이 production에는 아직 안 갔던 것. rsync로 동기화 후 재실행: Running upgrade c3d4e5f6a1b2 -> a6ae466f8b07, add_random_order_to_board_config (NEUTRALIZED) ✅ no destructive ops (NEUTRALIZED) 표시가 떠 있는 게 결정적 증거였다. production이 우리가 비운 새 파일을 정상적으로 읽고 있다는 뜻. 이제 진짜로 적용해도 안전하다. $ alembic upgrade head INFO Running upgrade c3d4e5f6a1b2 -> a6ae466f8b07, add_random_order_to_board_config (NEUTRALIZED) $ alembic current a6ae466f8b07 (head) DB 변경 0건, 데이터 손실 0건, 다운타임 0초. alembic_version 테이블의 한 행만 갱신되었다. 폭탄 해체 완료. 다음 편 — ③ 바이브코딩의 위험과 안전망 — 사람은 잊지만 코드는 잊지 않는다
이온디
이온디 1개월 전
시리즈 다음 편 → 바이브코딩의 위험성 ① — 어제는 분명히 괜찮았다 AI에게 코드를 맡기는 시대, 우리는 어떤 안전망을 가지고 있을까? 한 번의 자동화가 30개 테이블을 날릴 뻔한 이야기. — 시리즈 1편 / 사건 발견과 추적 평범한 아침, 작은 위화감 오늘 아침 관리자 화면에 접속했더니 몇몇 메뉴가 비어 있었다. SEO 설정, 재무 거래내역, 호스팅 생성 로그. 그리고 발송 로그도. 처음에는 단순히 "아직 데이터를 안 채워서 그런가 보다" 싶었다. 신규 기능 위주의 메뉴들이라서 그럴… 시리즈 다음 편 → 바이브코딩의 위험성 ① — 어제는 분명히 괜찮았다 AI에게 코드를 맡기는 시대, 우리는 어떤 안전망을 가지고 있을까? 한 번의 자동화가 30개 테이블을 날릴 뻔한 이야기. — 시리즈 1편 / 사건 발견과 추적 평범한 아침, 작은 위화감 오늘 아침 관리자 화면에 접속했더니 몇몇 메뉴가 비어 있었다. SEO 설정, 재무 거래내역, 호스팅 생성 로그. 그리고 발송 로그도. 처음에는 단순히 "아직 데이터를 안 채워서 그런가 보다" 싶었다. 신규 기능 위주의 메뉴들이라서 그럴 만도 했다. 그런데 이상했다. 며칠 전 분명히 본 적이 있는 데이터들이었다. 이게 왜 비어 있지? 처음 떠올린 가설은 단순했다. eondcms는 Rhymix(xe_* 테이블)를 그대로 쓰면서 신규 기능은 eond_* 테이블에 적재하는 구조다. 신규 테이블이 새로 만들어진 거라, 이전 데이터가 없는 게 자연스럽다. 깔끔한 설명이었다. 그런데 깔끔한 설명일수록 의심해야 한다는 걸, 8년 프리랜서 생활이 가르쳐준 본능이 있었다. "원래 있었다"는 증언 테이블 행 수를 직접 세어 봤다. eond_site — 4개 행. SEO는 사이트별로 채우면 되는 정상 상태. eond_bank_transactions — 0개. 이전엔 있었다. eond_hosting_setup_log — 0개. 이것도 있었다. "원래 있었다"는 기억이 옳다면, 신규 테이블 가설은 무너진다. 데이터가 사라진 것이다. 이쯤에서 가장 먼저 점검하는 건 MySQL의 binlog. 점-시간 복구(point-in-time recovery)가 가능한 유일한 안전망이다. SHOW VARIABLES LIKE 'log_bin'; OFF. 이 한 줄로 모든 복구 옵션이 사라졌다. 백업 디렉터리도 비어 있었다. 추적할 수 있는 흔적이 통째로 사라진 셈이다. 어제는 정상이었다 다행인지 불행인지, 사라진 건 "단순 로그 정도"였다. 글, 회원, 댓글, 파일 같은 핵심 데이터는 멀쩡했다. 다시 채워 넣으면 되는 수준. 그렇다고 그냥 넘어갈 일은 아니었다. 이런 사고는 다음에도 일어날 수 있고, 다음엔 운이 안 좋을 수도 있으니까. 추적의 단서를 찾기 위해 시점부터 좁혔다. 어제는 괜찮았던 걸로 기억함 이 한 줄이 결정적이었다. 어제까지 정상이었고, 오늘 사라졌다. 그렇다면 그 사이에 누군가 무엇을 했다. git log를 펼쳤다. efd252f 2026-04-28 14:51:03 feat: major update — inquiry-to-project flow, notification logging, market price, xe_ table integration 오늘 오후 2시 51분에 거대한 커밋이 하나 들어가 있었다. "major update"라는 말이 이미 의심스러웠지만, 그보다 눈에 띈 건 변경된 파일 수였다. 모델 추가, 라우터 추가, 서비스 추가, 마이그레이션 파일들 — 그중에서 한 줄이 유독 튀었다. 1091 +++ alembic/versions/a6ae466f8b07_add_random_order_to_board_config.py 마이그레이션 파일 하나가 1091줄. 게다가 이름은 "board_config에 random_order 추가". 컬럼 하나 추가하는 데 1091줄? 이름과 다른 내용 파일을 열어봤다. """add_random_order_to_board_config Revision ID: a6ae466f8b07 Create Date: 2026-04-27 16:36:16.654089 """ def upgrade() -> None: op.create_table('eond_hosting_plan', ...) op.drop_table('eond_project_quote_items') op.drop_table('eond_outreach_template') op.drop_table('eond_notification_log') op.drop_table('eond_marketing_activity') ... 이름은 컬럼 추가인데, 내용은 30개가 넘는 테이블을 DROP하는 코드였다. 사라진 테이블 목록을 훑어보니 정확히 오늘 사라진 데이터들이 거기에 있었다. eond_bank_transactions (재무 거래내역) ✓ eond_hosting_setup_log (호스팅 생성 로그) ✓ eond_clients (고객 관리) ✓ eond_audit_log, eond_blog_posts, eond_member_login_log eond_marketing_* 시리즈 거의 전부 eond_newsletter_*, eond_post_seo, eond_board_seo eond_publish_queue, eond_writing_material, eond_wiki_pages ... 범인을 찾았다고 생각했다. 어제(4/27 16:36) 만들어진 이 파일이, 오늘 배포(4/28 14:51) 시점에 production에서 실행되며 데이터를 다 날려버렸다고. 시간 순서도 완벽하게 맞아떨어졌다. 그런데 정말 그랬을까? 결정적 한 줄 확신을 굳히기 전에 production에서 alembic 상태를 확인했다. $ alembic current c3d4e5f6a1b2 이 한 줄이 모든 추리를 뒤집었다. c3d4e5f6a1b2는 폭탄 마이그레이션(a6ae466f8b07)의 바로 직전 단계였다. 즉 production은 아직 폭탄을 실행하지 않은 상태였다. 1091줄의 DROP 문은 production DB에 한 줄도 닿지 않았다. 그렇다면 오늘 비어버린 데이터는 누가 지웠나? 솔직히 말하자면 — 모른다. binlog가 꺼져 있어서 추적할 흔적이 없다. 어쩌면 어떤 수동 작업, 어쩌면 다른 환경과의 혼동, 어쩌면 우리가 모르는 어떤 reset 스크립트의 흔적. 영원히 미궁이다. 하지만 진짜 무서운 건 그게 아니다. 폭탄은 아직 안 터졌을 뿐, 신관은 빠져 있는 상태로 차에 실려 있다는 것. 다음 배포 때 누가 무심코 alembic upgrade head를 누르는 순간, 그제서야 30개 테이블이 진짜로 사라진다. 이번엔 운 좋게 발견했지만, 발견하지 못했다면? 이 시점부터 작업의 성격이 바뀌었다. 사고 조사가 아니라 사고 예방으로. 다음 편 — ② 범인은 autogenerate였다 — 폭탄 마이그레이션 해체
이온디
이온디 2년 전
1. phpstorm을 사용 중입니다. 2. 맥에서 mamp를 사용하고 있습니다. 3. 터미널의 PATH 환경 변수에 추가해줬습니다. (참고) phpstorm 실행 후 프로젝트 터미널에서 php -v 명령어를 입력하면 버전 정보 확인 가능합니다. 4. composer를 설치했습니다. (참고) phpstorm 실행 후 프로젝트 터미널에서 composer 명령어를 입력하면 composer 정보 확인 가능합니다. % composer --version Composer version 2.7.2 … 1. phpstorm을 사용 중입니다. 2. 맥에서 mamp를 사용하고 있습니다. 3. 터미널의 PATH 환경 변수에 추가해줬습니다. (참고) phpstorm 실행 후 프로젝트 터미널에서 php -v 명령어를 입력하면 버전 정보 확인 가능합니다. 4. composer를 설치했습니다. (참고) phpstorm 실행 후 프로젝트 터미널에서 composer 명령어를 입력하면 composer 정보 확인 가능합니다. % composer --version Composer version 2.7.2 2024-03-11 17:12:18 PHP version 8.2.0 (/Applications/MAMP/bin/php/php8.2.0/bin/php) Run the "diagnose" command to get more detailed diagnostics output. 5. 현재 프로젝트에 composer를 이용해서 rector를 설치했습니다. (참고) composer require rector/rector --dev composer.json 파일이 루트에 생성되었습니다. { "require-dev": { "rector/rector": "*" } } 6. 혹시나 몰라 phpstorm에서 php 설정이나 composer 설정도 확인을 했습니다. (참고) composer 실행 경로도 정상적입니다. 루트에 생성된 composer.json 파일은 phpstorm 이 실행되면 자동으로 인식되기도 하고, 따로 추가해주기도 했습니다. 7. 생성된 composer.json 상단에 Install 버튼을 실행해주면.. install --no-interaction   env: php: No such file or directory   Failed to install packages for ./rx/composer.json. 이런 오류가 Composer Log 탭에 출력됩니다. 맥에서 직접 PHP를 설치해준 것은 아니지만 MAMP의 PHP 파일을 실행하고 있습니다. 그래서 3번 과정에서 PATH에 환경변수도 추가해주었기 때문에 터미널에서나 phpstorm에서나 php -v 명령어를 입력하면 php 정보를 확인할 수 있습니다. 아직 rector.php 파일 생성 후 설정도 못했는데 여기서 막히네요. rector 사용해보기도 전에 설정 부터가 막막하군요. MAMP 대신 맥에서 바로 PHP 설치 후 실행해줬다면 이런 과정 없이 좀 더 수월했을 성 싶지만;;; 아마 composer 에서 php 경로를 못 찾아서 그런 거 같은데요, 일반적으로 Composer는 시스템의 PATH를 사용하여 PHP를 찾습니다. 따라서 PHP 경로를 별도로 Composer에 설정해주는 기능은 없습니다. 대신에 Composer는 시스템의 PHP 실행 파일을 사용하여 PHP 코드를 실행하고 Composer 자체적으로 PHP 경로를 지정하는 옵션은 제공하지 않습니다. 따라서 PHP 경로를 추가하려면 Composer를 실행하는 쉘 환경에서 PATH에 PHP 경로를 추가해야 합니다. 이를 위해 macOS에서는 보통 .bash_profile, .bashrc, 또는 .zshrc와 같은 쉘 설정 파일에 PHP 경로를 추가하여야 합니다. 해당 파일에 PHP 경로를 추가한 후에는 해당 쉘을 다시 실행하거나 source 명령어를 사용하여 설정 파일을 다시 로드해야 합니다. 만약 PHP 경로를 설정해줘도 여전히 Composer가 PHP를 찾지 못하는 경우, 환경 설정이나 권한 문제일 수 있으므로 해당 문제를 해결해야 합니다. 7번 문제 해결함.. /Users/eond/.zshrc 이 파일 수정함. export ANDROID_HOME=/Users/eond/Library/Android/sdk export PATH=$PATH:$ANDROID_HOME/emulator export PATH=$PATH:$ANDROID_HOME/tools #export PATH=$PATH:$ANDROID_HOME/tools/bin export PATH=$PATH:$ANDROID_HOME/platform-tools export PATH="$PATH:/Users/eond/development/flutter/bin" #eond edit 20220831 export NVM_DIR=~/.nvm source $(brew --prefix nvm)/nvm.sh export PATH="$PATH:/Applications/MAMP/bin/php/php8.2.0/bin" 그리고, 적용하니 정상적으로 phpstorm에서 composer 로 설치가 완료됨. (참고) 8번. 이제 rector.php 설정..
이온디
이온디 7년 전
댓글에서 평점을 입력받아 글보기 화면과 글 리스트 화면에 해당 점수 평점을 출력하는 게시판을 만들어보겠습니다. 본 팁은 댓글의 추천수를 활용하여 게시글의 평점을 매기는 시스템입니다. 고로, 댓글 자체의 추천 시스템은 활용할 수가 없습니다. 댓글 자체에 좋아요, 싫어요를 할 수 있는 voted_count와 blamed_count 컬럼 중 voted_count를 사용하기 때문에 그렇습니다. 그 밖에 활용할만한 카운트 컬럼이 없더군요 ㅠ 해당 팁은 기존 board 모듈의 콘트롤러를 수정하고 쿼리문을 … 댓글에서 평점을 입력받아 글보기 화면과 글 리스트 화면에 해당 점수 평점을 출력하는 게시판을 만들어보겠습니다. 본 팁은 댓글의 추천수를 활용하여 게시글의 평점을 매기는 시스템입니다. 고로, 댓글 자체의 추천 시스템은 활용할 수가 없습니다. 댓글 자체에 좋아요, 싫어요를 할 수 있는 voted_count와 blamed_count 컬럼 중 voted_count를 사용하기 때문에 그렇습니다. 그 밖에 활용할만한 카운트 컬럼이 없더군요 ㅠ 해당 팁은 기존 board 모듈의 콘트롤러를 수정하고 쿼리문을 추가하고 하는 식으로 개발된 팁입니다. 가능하신 분은 별도 모듈을 개발하셔서 댓글을 작성할 때 해당 모듈로 해당 함수를 호출하는 식으로 개발하시면 될듯합니다. ※ document.getDocumentList가 호출되는 시점에서 해당 모듈에 있는 함수로 트리거로 통해 바꿔치기 하는 식으로 개발하시면, 코어는 건드릴 필요없이 작업이 가능합니다. 아직 제 수준은 거기까지가 안되므로 ;ㅁ; 1. 수정할 파일목록 1) 댓글 쓰기 /modules/board/skins/sketchbook5_review/_comment_write.html 2) 글보기 파일 /modules/board/skins/sketchbook5_review/_read_review.html 3) 게시판 모듈 코멘트 쓰기 쿼리문 /modules/board/tpl/filter/insert_comment.xml 4) 게시판 모듈 콘트롤러 파일 /modules/board/board.controller.php 5) /modules/board/queries/insert_comment.xml 6) /modules/board/queries/plusPackageStar.xml 7) /modules/board/queries/voteAdd.xml 8) /modules/board/queries/voteRemove.xml 9) /modules/board/queries/voteSum.xml _comment_write.html <input type="hidden" name="star_point" value="" /> <ul class="starPoint"> <li><input type="radio" name="grd" id="grd1"><label for="grd1"><span class="grd grd1">1</span></label></li> <li><input type="radio" name="grd" id="grd2"><label for="grd2"><span class="grd grd2">2</span></label></li> <li><input type="radio" name="grd" id="grd3"><label for="grd3"><span class="grd grd3">3</span></label></li> <li><input type="radio" name="grd" id="grd4"><label for="grd4"><span class="grd grd4">4</span></label></li> <li><input type="radio" name="grd" id="grd5"><label for="grd5"><span class="grd grd5">5</span></label></li> </ul>코멘트 작성 form 안에 위 코드를 추가합니다. _read_review.html _read.html 파일에 인클루드되는 파일입니다. {@ $mi->tmb_effect='N'; if(!$mi->rd_tl_font) $mi->rd_tl_font='ngeb'; if(!$mi->rd_top_font) $mi->rd_top_font='ngeb'; if(!$mi->rd_btm_font) $mi->rd_btm_font='ngeb'; if(!$mi->prev_next_cut_size) $mi->prev_next_cut_size=60; $sns_link=$oDocument->getPermanentUrl().'?l='.$lang_type; $sns_title=urlencode($oDocument->getTitle()); } <include target="_read_cat.html" cond="$mi->default_style=='review'"/> <div class="lst_sort"> <a class="sort1" href="{getUrl('mid',$mid,'order_type','','sort_index','','document_srl','')}">인기</a> <a class="sort2" href="{getUrl('mid',$mid,'order_type','desc','sort_index','regdate','document_srl','')}">신규등록</a> <a class="sort3" href="?mid={$mid}&order_type=desc&sort_index=regdate&act=dispBoardTagList">브랜드별</a> <div class="cat_m">카테고리별</div> </div> <div class="rd<!--@if(!$mi->rd_nav_style)--> rd_nav_style2<!--@end--><!--@if($mi->default_style=='blog')--> rd_blog {$mi->blog_style}<!--@end--> clear" style="padding:{$mi->rd_padding};" data-docSrl="{$oDocument->document_srl}"> <!--// Header --> <div class="rd_hd clear"> <!--브랜드명 이미지--> <div class="brand"> <include target="./assets/brand.html" /> </div> <!--이미지 썸네일--> <div class="thumb"> <block loop="$oDocument->getUploadedFiles()=>$key,$file"> <!--@if($key==0)--> <block cond="!$mi->img_insert2"> {@ $ext=substr($file->source_filename, -4); $ext=strtolower($ext); $extImg=in_array($ext,array('.jpg','jpeg','.gif','.png')); } </block> <block cond="$mi->img_insert2"> {@ $ext=substr($file->source_filename, -15); $ext=strtolower($ext); $extImg=in_array($ext,array('_rd_gallery.jpg','rd_gallery.jpeg','_rd_gallery.gif','_rd_gallery.png')); } </block> <img cond="$extImg" src="{$file->uploaded_filename}" alt="" /> <!--@end--> </block> </div> <!--브랜드명 태그 --> {@ $tag_list=$oDocument->get('tag_list') } <div class="info_brandname">{$tag_list[0]}</div> <!--제목--> <div class="title">{$oDocument->getTitle()}</div> <!--별점--> {@ //별점포인트 $star_point = $oDocument->get('voted_count')/$oDocument->getCommentcount(); $star_point_format = number_format((float)$star_point, 2, '.', ''); //별점퍼센트 $starRating = $oDocument->get('voted_count')*10*2/$oDocument->getCommentcount(); } <div class="starbox"> <div class="star_rating"><span style="width:{$starRating}%"></span></div> <div class="star_point">{$star_point_format}점</div> <div class="star_comment">({$oDocument->getCommentcount()}명)</div> </div> <!--카테고리--> <div class="cat">{$category_list[$oDocument->get('category_srl')]->title}부문</div> <!--제품정보(사용자정의)--> <block loop="$oDocument->getExtraVars() => $key,$val"> <span class="info_size" cond="$val->eid=='info_size'">{$val->getValueHTML()}</span> </block> / <block loop="$oDocument->getExtraVars() => $key,$val"> <span class="info_size" cond="$val->eid=='info_price'">{$val->getValueHTML()}</span> </block> </div> <!--// Body --> <div class="rd_body clear"> <article>{$oDocument->getContent(false)}</article> <!--최저가 바로가기(사용자정의)--> <block loop="$oDocument->getExtraVars() => $key,$val"> <div class="info_link" cond="$val->eid=='info_link' && $val->getValueHTML()"> <a href="{$val->getValueHTML()}" target="_blank">최저가 바로가기</a> </div> </block> </div> <!--// Footer --> <div class="rd_ft"> <div class="starbox"> <div class="star_point">{$star_point_format}점</div> <div class="star_comment">({$oDocument->getCommentcount()}명 평가)</div> <div class="star_rating"> <span style="width:{$starRating}%"></span> </div> </div> <div class="graph_wrap"> {@ $oDB = &DB::getInstance(); $query = $oDB->_query('select * from xe_member_message where receiver_srl = '.$logged_info->member_srl.' AND message_type = "R" ORDER BY regdate DESC limit 0, 5'); $result = $oDB->_fetch($query); $oMemberModel =& getModel('member'); $member_info = $oMemberModel->getMemberInfoByMemberSrl($val->sender_srl); } {@ $oDB = &DB::getInstance(); $query5 = $oDB->_query('select count(*) as cnt from xe_comments where document_srl = '.$oDocument->document_srl.' and voted_count = 5'); $result5 = $oDB->_fetch($query5); $query4 = $oDB->_query('select count(*) as cnt from xe_comments where document_srl = '.$oDocument->document_srl.' and voted_count = 4'); $result4 = $oDB->_fetch($query4); $query3 = $oDB->_query('select count(*) as cnt from xe_comments where document_srl = '.$oDocument->document_srl.' and voted_count = 3'); $result3 = $oDB->_fetch($query3); $query2 = $oDB->_query('select count(*) as cnt from xe_comments where document_srl = '.$oDocument->document_srl.' and voted_count = 2'); $result2 = $oDB->_fetch($query2); $query1 = $oDB->_query('select count(*) as cnt from xe_comments where document_srl = '.$oDocument->document_srl.' and voted_count = 1'); $result1 = $oDB->_fetch($query1); } <div class="review_box"> <canvas id="chart-1"></canvas> </div> <script src="./assets/Chart.bundle.js"></script> <script src="./assets/utils.js"></script> <load target="js/chart.js" type="body" /> <script> var presets = window.chartColors; var utils = Samples.utils; var options = { maintainAspectRatio: false, spanGaps: false, elements: { line: { tension: 0.100001 } }, plugins: { filler: { propagate: false } }, scales: { xAxes: [{ // x 텍스트 display: true, ticks: { autoSkip: false, maxRotation: 0 }, //x 가이드라인 gridLines: { display:false } }], yAxes: [{ // y 텍스트 display: true, // y가이드라인 gridLines: { display:true } }] } }; [false, 'origin', 'start', 'end'].forEach(function(boundary, index) { new Chart('chart-' + index, { type: 'line', data: { //labels : ["최악","별로","쏘쏘","굿굿","짱짱"], labels : ["","","","",""], datasets: [{ backgroundColor: utils.transparentize(presets.red), borderColor: presets.red, //data : [2,6,9,26,45], data : [{$result1->cnt},{$result2->cnt},{$result3->cnt},{$result4->cnt},{$result5->cnt}], label: '', fill: boundary }] }, options: utils.merge(options, { title: { text: '', display: true }, //responsive: true, // 타이틀 legend: { display: false } }) }); }); </script> </div> </div> <!--// Read Footer Navi --> <div class="rd_ft_nav clear"> <a cond="$mi->default_style!='viewer' && $mi->rd_ft_nav" class="btn_img fl" href="{getUrl('document_srl','')}"><i class="fa fa-bars"></i> {$lang->cmd_list}</a> <!--// SNS small --> <include cond="!$mi->to_sns" target="_read_sns.html" /> <!--// Read Nav --> {@ $ft_read_nav=1} <include target="_read_nav.html" /> {@ $ft_read_nav=''} </div> <!--// Comment --> <block cond="$mi->cmt_wrt=='sns'"> {@ $mi->cmt_wrt_position=''; $mi->profile_img=''; } </block> <div cond="!$mi->viewer_cmt" class="fdb_lst_wrp {$mi->fdb_style} {$mi->profile_img}"> <div id="{$oDocument->document_srl}_comment" class="fdb_lst clear {$mi->fdb_nav} {$mi->cmt_wrt_position}"> <!--// Editor --> <!--@if($mi->cmt_wrt=='sns')--> <!--// SocialXE --> <div cond="$oDocument->allowComment() && $mi->select_editor!='N'" class="editor_select bubble fr m_no" title="{$lang->noti_rfsh}"> <a class="tg_btn2" href="#" data-href="#editor_select"><em class="fa fa-info-circle bd_info_icon"></em> {$lang->select_editor}</a> <div cond="$rd_idx==0" id="editor_select" class="editor_select_cnt tg_cnt2 wrp"><button type="button" class="tg_blur2"></button> <a class="on"|cond="$mi->cmt_wrt=='simple'" href="#" onclick="jQuery.cookie('bd_editor','simple');location.reload();return false"><em>✔ </em>{$lang->textarea}</a> <a class="on"|cond="$mi->cmt_wrt=='editor'" href="#" onclick="jQuery.cookie('bd_editor','editor');location.reload();return false"><em>✔ </em>{$lang->wysiwyg}</a> <a class="on"|cond="$mi->cmt_wrt=='sns'" href="#" onclick="jQuery.cookie('bd_editor','sns');location.reload();return false"><em>✔ </em>{$lang->sxc_editor}</a> <i class="edge"></i><button type="button" class="tg_blur2"></button> <!--// ie8; --><i class="ie8_only bl"></i><i class="ie8_only br"></i> </div> </div> <img class="zbxe_widget_output" widget="socialxe_comment" skin="sketchbook5" colorset="{$mi->colorset}" document_srl="{$oDocument->document_srl}" content_link="{getFullUrl('','document_srl',$oDocument->document_srl,'dummy','1')}" content_title="{htmlspecialchars($oDocument->getTitleText())}" enter_send="N" auto_view_sub="Y"|cond="!$mi->auto_view_sub" style="overflow:visible" /> <!--@else--> <!--// Comment Write : Top --> <include cond="$oDocument->allowComment() && !$mi->cmt_wrt_position" target="_comment_write.html" /> <!--// Comment List --> <div id="cmtPosition" aria-live="polite"><include target="_comment.html" /></div> <!--// Comment Write : Bottom --> <include cond="$oDocument->allowComment() && $mi->cmt_wrt_position=='cmt_wrt_btm'" target="_comment_write.html" /> <!--@end--> </div> </div> </div> <!--// 목록 보이지 않을 때 보이는 하단 메뉴 --> <div cond="$mi->rd_lst && $mi->default_style!='blog'" class="btm_mn clear" style="border-top:1px solid #CCC"> <div class="fl"> <a class="btn_img" href="{getUrl('document_srl','')}"><i class="fa fa-bars"></i> {$lang->cmd_list}</a> </div> </div> <hr id="rd_end_{$oDocument->document_srl}" class="rd_end clear" /> insert_comment.xml <filter name="insert_comment" module="board" act="procBoardInsertComment"> <form> <node target="star_point" required="true" /> <node target="document_srl" required="true" /> <node target="nick_name" required="true" maxlength="20"/> <node target="password" required="true" /> <node target="email_address" maxlength="250" filter="email" /> <node target="homepage" maxlength="250" filter="url" /> <node target="content" required="true" minlength="1" /> </form> <parameter> <param name="mid" target="mid" /> <param name="document_srl" target="document_srl" /> <param name="comment_srl" target="comment_srl" /> <param name="parent_srl" target="parent_srl" /> <param name="nick_name" target="nick_name" /> <param name="password" target="password" /> <param name="email_address" target="email_address" /> <param name="homepage" target="homepage" /> <param name="content" target="content" /> <param name="is_secret" target="is_secret" /> <param name="notify_message" target="notify_message" /> <param name="star_point" target="star_point" /> </parameter> <response callback_func="completeInsertComment"> <tag name="error" /> <tag name="message" /> <tag name="mid" /> <tag name="document_srl" /> <tag name="comment_srl" /> </response> </filter>코멘트를 작성할 때 star_point를 추가해주는 파일입니다. <node target="star_point" required="true" /> <param name="star_point" target="star_point" /> 기본 inset_comment.xml 에서 이 부분만 추가되었습니다. board.controller.php 게시판 모듈 콘트롤러 파일입니다. 코멘트를 작성할 때도 동작하는 파일입니다. 334번째 줄에 다음과 같은 구문을 찾아주세요. if($comment->comment_srl != $obj->comment_srl) { ... } 오른쪽 처럼 수정하시면 됩니다. 336줄 $obj->voted_count = $obj->star_point; 349줄 $obj->voted_count = $obj->star_point; 369줄 $star_obj->module_srl = $this->module_srl; $star_obj->document_srl = $obj->document_srl; $star_obj->voted_count = $oDocument->get('voted_count')+$obj->star_point; $output = executeQuery('board.voteAdd', $star_obj); //$star_args->package_srl = $args->package_srl; //$star_args->voted = $package->voted+$args->star_point; //$output = executeQuery('resource.plusPackageStar', $star_args); //$output = executeQuery('document.procDocumentVoteUp', $star_obj); // $document_srl = Context::get('target_srl'); // $oDocumentModel = getModel('document'); // $oDocumentController = getController('document'); // $document_srl = Context::get('document_srl'); // return $oDocumentController->updateVotedCount($document_srl); board.controller.php에서 코멘트 삭제시 차감하는 코드도 추가해야합니다. function procBoardDeleteComment() 부분을 찾아서 반드시 그 바로 아래 첫번째줄에 작성해야합니다. // 댓글 삭제 시 게시물 추천수 차감하기 $obj = Context::getRequestVars(); $obj->module_srl = $this->module_srl; $document_srl = Context::get('document_srl'); $oDocumentModel = &getModel('document'); $oDocument = $oDocumentModel->getDocument($document_srl); $comment_srl = Context::get('comment_srl'); $oCommentModel = getModel('comment'); $oComment = $oCommentModel->getComment($comment_srl, FALSE, FALSE); $star_obj->module_srl = $this->module_srl; $star_obj->document_srl = $obj->document_srl; $star_obj->voted_count = $oDocument->get('voted_count')-$oComment->get('voted_count'); $output = executeQuery('board.voteAdd', $star_obj); <스크린샷> 1) 평점+댓글쓰기 화면 2) 리스트 화면 - 글정렬 순서를 추천수+내림정렬로 하면 됩니다.
이온디
이온디 7년 전
XE관리자 커스텀 CSS 입니다. CSS @charset "utf-8"; @font-face { font-family: 'NotoSansDemiLight'; font-style: normal; src: url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-DemiLight.eot'); src: url(… XE관리자 커스텀 CSS 입니다. CSS @charset "utf-8"; @font-face { font-family: 'NotoSansDemiLight'; font-style: normal; src: url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-DemiLight.eot'); src: url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-DemiLight.eot') format('embedded-opentype'), url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-DemiLight.woff') format('woff'), url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-DemiLight.otf') format('truetype'); } @font-face { font-family: 'NotoSansMedium'; font-style: normal; src: url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Medium.eot'); src: url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Medium.eot') format('embedded-opentype'), url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Medium.woff') format('woff'), url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Medium.otf') format('truetype'); } @font-face { font-family: 'NotoSansBlack'; font-style: normal; src: url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Black.eot'); src: url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Black.eot') format('embedded-opentype'), url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Black.woff') format('woff'), url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Black.otf') format('truetype'); } @font-face { font-family: 'NotoSansBold'; font-style: normal; src: url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Bold.eot'); src: url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Bold.eot') format('embedded-opentype'), url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Bold.woff') format('woff'), url('https://021b64efda903fd2f04226783d09490774fac328.googledrive.com/host/0B5GXa347KCFFVDYydUw4NGwybTg/fonts/NotoSansCJKkr-Bold.otf') format('truetype'); } /*폰트*/ body>.x, .x table, .x input, .x textarea, .x select, .x button, html, body, table, input, textarea, select, button{margin:0;padding:0;font-family: 'Noto Sans KR', sans-serif !important; } /*컨텐츠넓이*/ body>.x:first-child{max-width:inherit !important;} body{margin-left:0;} /*메뉴 그림자 및 메뉴 레이아웃 */ body .x>.header:before{box-shadow:none;} body .x>.body>.gnb>ul{box-shadow:none;border:0;} .x>.body{padding-bottom:0 !important;} .x>.body>.gnb{height:100vw;background:#272634;} .x>.body>.gnb{margin-top:0 !important;} .x>.body>.gnb>ul>li[data-index="5"]{margin-bottom:0 !important;} /*메뉴 선택자 배경*/ body .x>.body>.gnb>ul>li, body .x>.body>.gnb>ul>li.open>a, body .x>.body>.gnb>ul>li.active{background:#272634;} .x>.body>.gnb>ul>li{border-top:0 !important;} /*메뉴 최상단 배경*/ .x>.body>.gnb>ul>li>a{background:#1e1e28 !important;color:#ccc;text-shadow:none;} .x>.body>.gnb>ul>li{border-bottom:1px solid #1e1e28 !important;} .x a[target="_blank"]:after, .x>.body>.gnb>ul>li>a>i, .x .dashboard>div>section>h2:before{ background-image: url(../img/glyphicons-halflings-white.png) !important; } /*업데이트알림*/ body .message.update{display:none;} /*대쉬보드 넓이*/ .x .dashboard>div{ max-width: calc(100% / 3); float: left !important; margin-right: 10px; } /*대쉬보드 상단*/ body .x>.header { border-bottom: 0px solid #ddd; background-color: #1e1e28; } body .x>.header>h1>a{color:#eee} /*상단 1픽셀*/ body .x>.skipNav>a{height:0;}
이온디
이온디 8년 전
https://www.slideshare.net/applseed/ui-ui https://www.slideshare.net/applseed/ui-ui" style="height: 563px;">https://www.slideshare.net/applseed/ui-ui 자주 사용되는 UI 패턴(컴포넌트) 과 UI 레이아웃 from Dongsik Yang https://www.slideshare.net/applseed/ui-ui https://www.slideshare.net/applseed/ui-ui" style="height: 563px;">https://www.slideshare.net/applseed/ui-ui 자주 사용되는 UI 패턴(컴포넌트) 과 UI 레이아웃 from Dongsik Yang
이온디
이온디 8년 전
'이 상한 치과'... 이렇게 해야 바가지 피할 수 있어 글 | 김민희주간조선 기자 사례 1 40대 직장인 이상호(가명)씨는 최근 사랑니 통증으로 종로구에 있는 A치과를 찾았다. 치아관리 모범생인 이씨가 스케일링 이외의 목적으로 치과를 찾은 건 처음이다. 매년 스케일링을 받는 단골 B치과는 예약이 밀려 버스광고에서 본 A치과가 생각나 무작정 찾아갔다. 치과에 들어서자 여성 상담사가 반갑게 맞더니 엑스레이 촬영과 치아 전체 촬영을 착착 진행했다. 상담사는 촬영 사진을 보여주며 “아… '이 상한 치과'... 이렇게 해야 바가지 피할 수 있어 글 | 김민희주간조선 기자 사례 1 40대 직장인 이상호(가명)씨는 최근 사랑니 통증으로 종로구에 있는 A치과를 찾았다. 치아관리 모범생인 이씨가 스케일링 이외의 목적으로 치과를 찾은 건 처음이다. 매년 스케일링을 받는 단골 B치과는 예약이 밀려 버스광고에서 본 A치과가 생각나 무작정 찾아갔다. 치과에 들어서자 여성 상담사가 반갑게 맞더니 엑스레이 촬영과 치아 전체 촬영을 착착 진행했다. 상담사는 촬영 사진을 보여주며 “아픈 사랑니가 90도로 기울어져 있어 뽑을 때 감염이 우려된다. 스케일링부터 해야 한다”고 했다. 6개월 전에 스케일링을 받은 이씨는 내키지 않았지만 ‘감염’이라는 말에 겁이 나 스케일링에 동의했다. 상담사는 이번엔 CT 촬영을 권유했다. 이씨는 “B치과는 CT 촬영 얘기가 없었다. 꼭 해야 하나?”고 되물었지만 “신경 위치를 알 수 없으면 수술 중 어떤 일이 벌어질지 모르기 때문에 위험하다”는 상담사의 말에 CT 촬영도 동의했다. 이번에는 수술 방법. 상담사는 “우리 병원에서는 최신 수술법인 물방울 레이저로 잇몸을 절개한다”며 추가요금이 나온다고 했다. ‘칼 절개냐, 물방울 레이저 절개냐’의 선택이 아니라 일방적 통보 분위기라 또 동의했다. 이게 다가 아니었다. 상담사는 치아 촬영 사진을 보여주며 “어금니 5개 충치를 치료해야 한다. (바늘 끝만 한 검은 부분을 확대해 보여주며) 겉으로는 멀쩡하지만 확대해서 보면 충치가 심각하다. 썩은 부위를 파내고 충전재로 채워야 한다”고 충치 치료를 권유했다. 충치 치료비 견적은 125만원. 치아 하나당 25만원이었다. 이씨는 충치 치료는 미루고 사랑니 발치만 하기로 했다. 발치를 위해 수술대에 누워서야 이씨는 치과의사를 대면했다. 하지만 수술이 끝날 때까지 이씨는 치과의사의 얼굴도 제대로 못 봤고, 목소리도 듣지 못했다. 수술 중 의사가 환자에게 한마디도 걸지 않은 데다가, 조명이 강해 의사 얼굴을 쳐다보기 힘들었기 때문이다. 이날 이씨가 사랑니 하나 발치에 든 비용은 18만원. ‘물방울 레이저 절개’ 추가 비용이 생각보다 컸다. 이씨는 “미용실에서 가위를 쓰든, 바리캉을 쓰든 미용사의 마음 아니냐, 바리캉으로 커트를 한다고 돈을 더 내는 법이 있냐?”며 따졌지만 상담사는 흔들림 없는 목소리로 말했다. “칼로 절개하면 감염 위험이 있지만 물방울 레이저는 그런 위험이 없다. 손님 수술이 잘된 건 물방울 레이저 절개 때문이다.” 뭔가 찜찜하다고 느낀 이씨는 단골 B치과를 찾았다. B치과에서는 이씨에게 “치료할 만한 충치가 하나도 없다”고 진단했다. “검은 점이 충치는 맞지만 아직 치료할 단계는 아니다”라고 했다. 얼마 후 이씨는 종합건강검진 구강검진에서도 ‘치아상태 양호’ 진단을 받았다. 치과의사는 이씨에게 “어떻게 이렇게 치아 관리를 잘하셨냐”며 칭찬했다. 이씨는 가슴을 쓸어내렸다. A치과 상담사의 권유대로 했다면 125만원을 들여 치료할 필요가 없는 치아 5개를 갈아낼 뻔했다. 사례 2 초등학교 4학년 박성우(가명)군은 충치 치료를 위해 성동구에 있는 C치과를 찾았다. 정부에서 전체 초등학생을 대상으로 한 ‘무료 구강검진’ 당시 어금니 충치 진단을 받았기 때문이다. 육안으로 작은 점만 한 크기인데, 치과의사는 충치 부분을 파내고 충전재로 메워야 한다고 했다. 충전재 설명은 간호조무사가 맡았다. 간호조무사가 권유한 충전재는 레진. 아말감은 보험이 적용돼 7000~8000원 수준으로 저렴하지만 수은 중독이 우려되는 데다 미관상 좋지 않아 ‘요즘 엄마’들은 잘 쓰지 않는다고 했다. 레진은 치아 하나당 10만원. 박군의 엄마는 10배가 넘는 비용이 부담스러웠지만 레진을 선택했다. “아말감으로 해주세요”라고 말했다간 아이를 사랑하지 않고 돈만 아끼려는 이기적인 엄마가 되는 분위기였다. 치과의사는 충치 치료 전 박군의 치아 엑스레이 촬영을 권했다. 의사는 박군의 치아 사진을 보여주더니 “송곳니의 ‘예방적 교정’이 필요하다”고 했다. 현재 송곳니 유치는 빠진 상태인데, 빠진 부분의 치아 사이 간격이 새로 올라오는 영구치보다 1㎜ 좁기 때문에 덧니가 될 가능성이 있다는 이유였다. 의사는 “덧니가 된 후 교정을 하면 시간도 많이 걸리고 아이도 힘들지만, 양쪽 치아를 벌려 놓는 예방적 교정을 하면 시간도 얼마 안 걸리고 아이도 별로 힘들지 않다”고 말했다. 비용을 묻자 의사는 “간호사에게 설명을 들으시라”고 보냈다. 간호사가 제시한 ‘예방적 교정’ 비용은 500만~600만원. 박군의 엄마가 “예방적 교정인데 왜 이렇게 비싸냐?”고 묻자 간호사는 “예방적 교정이나 덧니 교정이나 비용은 같다”며 하나마나한 답변만 내놨다. 박군은 예방적 교정을 하지 않았다. 1㎜의 오진을 기대했다. 그로부터 1년 후, 박군은 새 송곳니가 났다. 엑스레이 사진 분석을 통한 우려와 달리 고르고 예쁘게 자리 잡았다. 결국 의사가 말한 ‘예방적 교정’은 전혀 필요없었다. 혹시나 하는 걱정 때문에 예방적 교정을 했다면 어떻게 됐을까? 돈은 돈대로 내고, 아이는 아이대로 고통스러웠을 것이다. 그리고 의사는 “거 봐라. 내 말대로 예방적 교정을 해서 이렇게 치아가 고르게 나지 않았냐?”라고 말했을 것이다. 치과 과잉진료의 대표적 사례들이다. 주변을 둘러보면 치과에 대한 불신이 이만저만이 아닌 걸 확인할 수 있다. ‘과잉진료를 하지 않습니다’를 광고처럼 내건 치과도 꽤 있고, 포털사이트 ‘네이버’에 검색되는 ‘믿음치과’만 49곳에 달한다. 치과 불신시대를 역으로 보여주는 현실이다. ‘과잉진료 안 하는 치과’를 공유하는 카페도 속속 등장한다. 치과의사 불신은 치과 내부에서도 마찬가지다. 강남의 M치과 김소희(가명) 원장은 “나도 치과의사지만, 치과의사를 믿을 수 없다”며 “주변에서 믿을 만한 치과 선택법을 알려 달라고 하면 아무 곳이나 무작정 가지 말고 지인이나 단골을 찾아가시라고 한다”고 털어놨다. 치료비 편차가 치과만큼 큰 병원도 드물다. 주간조선은 가격 비교를 위해 서울시내 치과 여덟 곳의 견적을 비교해 보았다. 착한치과로 유명한 서울 마포구의 그린서울치과, 성동구의 동네치과 두 곳(행당동 S치과, 성수동 K치과) 등 상대적으로 치료비가 저렴한 네 곳과 서울 강남구의 Y치과, 서울 종로구의 R치과 등 럭셔리 시설을 갖춘 네 곳이 대상이었다. 견적 비교 결과 충치 하나 치료하는 데 드는 비용은 최저 6000원에서 최대 25만원으로 40배 넘는 차이가 났다. 같은 재료라도 가격 편차가 컸다. 보험이 적용되는 아말감이나 GI(글라스 아이오노머)로 할 경우는 최저 6000원에서 최고 1만원으로 두 배 정도 차이 났지만, 비보험 재료인 레진의 경우 최저 5만원에서 최고 25만원까지 5배의 차이를 보였다. 사랑니 발치는 최저 2만3000원에서 최고 18만원까지 8배가 차이 났다. 이번 비교 견적 대상 치과는 아니지만, 사랑니 발치에 100만원이 넘는 패키지 상품을 선보이는 강남의 치과도 있다. 기본 패키지는 80만원(국산 국소마취제+국산 봉합사+일반 부분마취), 고급 패키지는 100만원(수입 국소마취제+수입 봉합사+진통제 주사), 로열패키지는 150만원(수면마취+수술 후 회복실에서 진통제와 링거)이다. 최저가 사랑니 발치(2만3000원)와 최고가 사랑니 발치(130만원)가 무려 56배 차이다. 환자는 답답하다. 치과의사의 말을 믿어도 될지, 각기 다른 치과의사의 진단 중 누구의 말을 들어야 할지, 치료 재료는 무엇을 선택해야 할지…. 온통 선택거리 투성이다. 치과처럼 소비자의 적극적 선택을 요구하는 진료도 드물다. 선택의 기로마다 환자는 혼란스럽다. 생사를 가르는 치료가 아니다 보니 차일피일 미루다 치과 발길을 끊어버리기도 한다. 30대 직장인 김모씨가 그 경우다. 김씨의 말이다. “충치가 있다는 걸 알았지만 치과에 안 가게 된다. 큰맘 먹고 치과에 한번 갔다가 치료 비용이 너무 비싸서 그냥 나왔다. 다른 치과도 가 보고 치료받으려 했는데 회사 일이 바쁘다 보니 잊어버렸다.” 치과 진료비의 편차가 큰 원인에 대해 전문가는 두 가지로 꼽는다. 하나는 네트워크 치과의 유행이고, 또 하나는 임플란트의 대중화다. 1990년대 중후반에 출현한 네트워크 치과는 치과 불신 현상을 촉발한 주범으로 거론된다. 물론 네트워크 치과의 긍정적 측면도 많다. 브랜드를 공유하면서 진료시스템, 진료철학 등을 나누고, 공동구매와 공동마케팅을 통해 원가를 절감할 수 있다. 실제로 공동연구를 통해 새로운 치료 방법을 개발하는 곳도 속속 생기고 있다. 네트워크 치과 자체는 죄(罪)가 없다. 문제는 네트워크 병원의 이윤 추구형 시스템이다. 네트워크 치과는 실소유주가 따로 있고 치과의사, 간호조무사, 치과기공사 등 의료진을 기용해서 운영하는 경우가 많다. 이 경우 기본급은 아예 없거나 매우 적어 성과급제로 운영하는 것이 일반적이다. 진료를 많이 할수록 직원에게 이득이 돌아가는 구조다. 반값 임플란트 등의 저가정책을 내세워 환자를 유인하는 것이 네트워크 치과의 마케팅 기법이다. 김소희 원장은 “일단 네트워크 치과는 조심할 필요가 있다”고 조언한다. “주치의의 개념이 아니라 같은 브랜드의 네트워크 병원 내부에서 순환 근무를 하는 곳이 있기 때문에 책임 치료가 불가능하다”는 이유다. 의사가 자주 바뀌니 문제가 생겨도 AS를 요구하기 힘들다는 얘기다. 임플란트의 대중화는 진료비 편차를 벌려놓은 또 다른 원인이다. 임플란트 초기만 해도 임플란트 하나를 심는 데 300만원을 육박했다. 하지만 임플란트를 제작하는 업체도 많아지고 종류도 많아지고 시술환자도 늘어나면서 가격이 크게 낮아졌다. 100만원 이하를 받는 곳도 흔하다. 임플란트 대중화는 네트워크 치과와 불가분의 관계에 있다. 임플란트가 절실한 환자 중에는 경제적 여건이 어려운 노인들이 많다. 이들을 대상으로 네트워크 치과에서 반값 임플란트를 내세워 가격 덤핑을 하면서 치료비 편차가 커졌다. 치과의사 입장에서는 할 말이 많다. 매년 700~800개의 치과가 문을 닫는다. 2010~2012년 3년간 3444곳이 개원했고, 같은 기간 2321곳이 폐원했다. 치과는 다른 분야에 비해 건강보험 보장률이 낮다. 전체 질병의 건강보험 보장률(60~65%)의 절반인 30%에 불과하다. 감기 치료의 경우 국민건강보험에서 70%의 치료비를 보장해주는 것과 비교된다. 생사가 좌우되는 분야가 아니다 보니 상대적으로 지원율이 낮은 것이다. 치과의사 입장에서는 건강보험에서 지급해주는 돈이 너무 적어 보험 치료만으로는 치과 운영이 힘들다. 취재 과정에서 만난 치과의사들은 하나같이 “정직한 진료만 고집하면 치과가 망할 수밖에 없다”고 토로했다. 우리나라의 의료 시스템 자체가 치과의 과잉진료를 부른다는 얘기다. 동네 치과가 사랑니 발치를 꺼리는 것도 같은 이유다. 사랑니 발치의 경우 약 10만원의 진료비 중 7만원은 건강보험공단에서 부담하고 나머지 3만원이 환자 본인 부담금이다. 치아색 재료(레진이나 세라믹)로 충치 치료를 할 경우 10만~15만원을 버는 것과 비교해 형평성이 안 맞는다. 레진 충치 치료와 사랑니 발치는 시간과 난이도 면에서 비교가 안 된다. 사랑니 발치는 시간도 많이 걸리고(최대 30분) 수술 난이도도 높은 데다가 수술 후 부작용과 위험성도 크다. 치과 입장에서는 경영에 별 도움이 안 되면서 리스크만 큰 치료다. 우리동네 좋은치과는 과연 착할까? 믿을 만한 치과를 추천해주는 공신력 있는 기관은 없을까? 건강보험심사평가원(심평원) 측에 문의하자, 상담사는 “특정 병원을 추천해주는 것은 의료법 위반이라 할 수 없다”고 답했다. 인터넷 카페 여기저기에서 공유지식으로 ‘착한치과 리스트’를 모으고 있으나 신뢰도에 한계가 있다. 대한치과의사협회(협회장 최남섭·이하 대한치의협)에서는 올 8월부터 ‘우리동네 좋은치과’ 캠페인을 실시한다. 11월 11일 현재 대한치의협에 등록된 우리동네 좋은치과는 855곳. 참가 신청은 1000여곳이 넘는다. 전국 1만6000여곳의 치과 중 5~7%가 등록돼 있거나 등록 예정 중이다. 하지만 우리동네 좋은치과가 과연 착한지는 알 수 없다. ‘우리동네 좋은치과’는 등록제이지 인증제의 개념은 아니다. 자격 요건을 갖췄는지 여부를 심사하는 외부 기관이 따로 없다. 대한치의협에 성실히 연회비 30만원을 납부하고 건전한 치과 문화를 만들어가겠다는 자정적인 약속을 하는 캠페인 차원이다. 치과 내부에서 과잉진료를 안하고 양심진료를 하겠다는 자정적인 캠페인을 벌이는 것은 환영할 만하지만 ‘좋은치과’의 기준이 모호하다는 비판의 목소리도 터져나온다. “우리동네 좋은치과가 있다면 우리동네 나쁜치과도 있지 않나”며 “캠페인에 등록만 한다고 면죄부를 받고 거듭날 수는 없다”(박용호 치과신문 논설위원)는 날선 비판도 있다. 치과를 사용하는 가장 현명한 방법은 단골 치과를 만들어 꾸준히 자신의 치아 상태를 파악하고 있는 것이다. 믿고 다닐 만한 곳이 없다면 동네 치과를 단골로 만드는 것도 좋다. 과잉진료를 권하는 치과의사의 변명은 “환자가 언제 다시 치과에 올지 몰라서”였다. 충치를 수년간 방치했다간 호미로 막을 걸 가래로 막게 된다는 이유다. 2013년부터 스케일링도 연 1회 보험이 적용된다. S병원 서모 원장은 “바쁜 현대인들이 6개월마다 치과 검진을 받는 건 쉬운 일이 아니다”라며 “자신의 생일을 치과 검진일로 정해두고 생일 때 스케일링을 받으면서 한 번, 6개월 후 한 번 치과 검진을 받을 것을 권한다”고 말했다. 좋은 치과 구별법 1. 자연치아를 가능한 한 남기려고 노력하는가. 자연치아는 각종 인공치아보다 우수하다. 치주염이 심해 잇몸뼈 상당 부분이 망가져 인접 치아의 잇몸뼈마저 파괴하거나 치아 뿌리가 부러졌거나 신경치료가 불가능한 경우가 아니라면 자연치아를 살릴 수 있다. 충치가 심한 자연치아는 살릴 수 있는 가능성이 크다. 어떻게든 자연치아를 살리려는 노력 없이 자연치아를 뽑고 임플란트를 손쉽게 권한다면 좋은 의사가 아니다. 2. 스케일링이나 검진을 무료로 해주는 등 미끼 상품을 내세우지 않는가. 의료계에서 미끼 상품을 통한 영리 추구가 문제시된 적이 종종 있다. 이는 환자 유인 행위로 호객행위다. 진료 자체에는 상업성이 끼어들어선 안 된다. 공짜 심리를 이용해서 환자들을 유인하는 의료인이 환자 중심의 윤리적인 진료를 하리라고 기대하기는 어렵다. 3. 광고를 과도하게 하지 않는가. 전문의약품 광고 제한은 정보 제공이라는 순기능보다 오남용이라는 역기능을 불러올 가능성이 크기 때문이다. 의료기관 광고도 같은 맥락이다. 인터넷 포털사이트를 검색하면 광고비를 많이 내는 의료기관들이 먼저 뜬다. 막대한 광고비를 어디서 충당할지 생각해보라. 4. 지나치게 낮은 가격을 전면에 내세우지 않는가. 이런 경우 대대적인 광고도 병행하는 경우가 많다. 의료는 박리다매에 한계가 있다. 한 의사가 정해진 시간에 치료할 수 있는 환자 수는 제한돼 있기 때문이다. 환자 진료는 1+1 행사나 바겐세일하듯 할 수 있는 분야가 아니다. 낮은 가격을 전면에 내세우는 치과들은 보험진료 회피, 부실진료, 과잉진료 등 비윤리적 의료행태를 많이 보인다. 5. 치과위생사나 간호조무사가 치과의사보다 먼저 환자를 보고 진단하고 설명하지는 않는가. 치과 치료 과정에서 가장 중요한 과정은 진단이다. 정확한 진단이 치료로 이어지기 때문이다. 의사가 아닌 치과위생사나 간호조무사가 환자를 진단하는 행위는 의료법상으로 불법이다. 자료 : ‘이 상한 나라의 치과’(개마고원) 출처 | 주간조선 2382호 출처 : http://pub.chosun.com/client/news/viw.asp?cate=C01&mcate=M1003&nNewsNumb=20151118814&nidx=18815
이온디
이온디 9년 전
레이아웃을 만들다보면 XE의 템플릿 문법이 적용이 안되는 경우가 있습니다. cond는 되는데 loop문은 안된다던가, 이럴 때 테스트하는 방법입니다. 1. 레이아웃에 작성된 코드를 다 지우고 간단한 블럭 태그로 테스트를 해본다. <block cond="$logged_info">로그인됨</block> 2. 모두 지우고 안되는 부분만 작성해본다. 이렇게 확인해본바 저의 경우는 다음 코드 때문에 안되더군요. 안되던 코드 삽입해봅니다. <style>.uchat_wrap4744… 레이아웃을 만들다보면 XE의 템플릿 문법이 적용이 안되는 경우가 있습니다. cond는 되는데 loop문은 안된다던가, 이럴 때 테스트하는 방법입니다. 1. 레이아웃에 작성된 코드를 다 지우고 간단한 블럭 태그로 테스트를 해본다. <block cond="$logged_info">로그인됨</block> 2. 모두 지우고 안되는 부분만 작성해본다. 이렇게 확인해본바 저의 경우는 다음 코드 때문에 안되더군요. 안되던 코드 삽입해봅니다. <style>.uchat_wrap47445045_super {width:100px;height:550px;margin:0 !important;padding:0 !important;background:#ccc;overflow:hidden;}#uchat_wrap47445045, #uchat_wrap47445045 *, #uchat_wrap47445045 table, #uchat_wrap47445045 tr, #uchat_wrap47445045 td, #uchat_wrap47445045 input, #uchat_wrap47445045 label {width:auto;margin:0;padding:0;font-family:"Apple SD Gothic Neo","malgun gothic","nanumgothic"; font-size:12px; -ms-user-select:none; -moz-user-select:none; -webkit-user-select:none;font-size:12px;line-height:1.4;border:0;text-align:left;font-weight:normal;min-height:0;min-width:0;color:gray;}#uchat_wrap47445045 {width:98px;height:548px;position:relative; margin:1px;}#uchat_wrap47445045 .uchat_middle {background:#e1e1e1;height:499px;width:98px;overflow:hidden;}#uchat_wrap47445045 .uchat_middle:after{content:""; display:block; clear:both; height:0; visibility:hidden;}#uchat_wrap47445045 .uchat_middle .conversation_contents {word-wrap: break-word;background:white;overflow-y:scroll;-webkit-overflow-scrolling: touch;overflow-x:hidden;}#uchat_wrap47445045 .uchat_middle .conversation_contents .loading {padding:2px 0;font-size:12px;word-wrap:break-word;}#uchat_wrap47445045 .uchat_middle .conversation_contents .cs_contents {font-size:12px;}#uchat_wrap47445045 .uchat_middle .conversation_contents .conversation_nick {font-weight:bold;cursor:pointer;font-size:12px;line-height:1;}#uchat_wrap47445045 .uchat_middle .conversation_contents .user_conversation {padding:2px;font-size:12px;}#uchat_wrap47445045 .uchat_middle .conversation_contents .user_conversation span {}#uchat_wrap47445045 .uchat_middle .conversation_contents .user_conversation>span>img {max-width:100px; max-height:20px;}#uchat_wrap47445045 .uchat_middle .conversation_contents .user_conversation>img {max-width:25px; max-height:25px;vertical-align: middle;}#uchat_wrap47445045 .uchat_middle .conversation_contents .user_conversation span, #uchat_wrap47445045 .uchat_middle .conversation_contents .user_conversation img {}#uchat_wrap47445045 .uchat_middle .conversation_contents .notification {padding:2px 0;color:green;text-align:center;font-size:12px;}#uchat_wrap47445045 .uchat_middle .conversation_contents .system {padding:2px 0;color:red;margin:2px;text-align:left;font-size:12px;}#uchat_wrap47445045 .uchat_middle .conversation_contents .error {padding:2px 0;text-align:center;font-size:12px;}#uchat_wrap47445045 .uchat_middle .topbar {height: 38px;line-height:38px;overflow:hidden;padding:0 4px 0 14px;color:#535353;font-size:12px;font-weight:bold;border-bottom:1px solid #E4E8EC;background-color:#FFFFFF;}#uchat_wrap47445045 .uchat_middle .topbar .count {float:left;font-size:12pt;line-height:32px;overflow:hidden;height:32px}#uchat_wrap47445045 .uchat_middle .topbar a.setting_icon {float:right;width:18px;height:18px;background:url(../minitalk/skin/default/images/setting_icon.gif) no-repeat 0 0;cursor:pointer;margin:10px 13px 0 0;}#uchat_wrap47445045 .uchat_middle .topbar a:hover {border:0;text-decoration:none;}#uchat_wrap47445045 .uchat_middle .member_list {overflow-x:hidden; overflow-y:scroll;-webkit-overflow-scrolling: touch;background:white;}#uchat_wrap47445045 .uchat_middle .member_list .user {padding:2px;font-size:12px;white-space:nowrap;overflow:hidden;cursor:pointer;background:white;vertical-align: middle;}#uchat_wrap47445045 .uchat_middle .member_list .user>span>img {max-width:100px; max-height:20px;display:inline}#uchat_wrap47445045 .uchat_middle .member_list .user>img {max-width:25px; max-height:25px;display:inline}#uchat_wrap47445045 .uchat_middle .member_list .user span {height:12px;}#uchat_wrap47445045 .uchat_middle .member_list .user span, #uchat_wrap47445045 .uchat_middle .member_list .user img {vertical-align: middle;font-size:12px;}#uchat_wrap47445045 .uchat_middle .member_list .my_members, #uchat_wrap47445045 .uchat_middle .member_list .admin_members, #uchat_wrap47445045 .uchat_middle .member_list .is_members, #uchat_wrap47445045 .uchat_middle .member_list .no_members {font-size:0;line-height:0;}#uchat_wrap47445045 .user_menu_background {position:absolute;left:0px;top:0;width:98px;height:548px;background:#e1e1e1;z-index:1000;display:none;}#uchat_wrap47445045 .user_menu {padding:5px 3px;background:#fff;display:none;position:absolute;border:2px solid #a0dbc7;z-index:1100;}#uchat_wrap47445045 .user_menu .menu_list {padding:3px 0;font-size:12px;cursor:pointer;}#uchat_wrap47445045 .user_menu .menu_list .admin_login {width:112px;height:20px;}#uchat_wrap47445045 .user_menu .user_nick {background:#f5f5f5;font-size:12px;}#uchat_wrap47445045 .input { height:24px;width:98px;overflow:hidden;text-align:center;background:white;}#uchat_wrap47445045 .input .conversation {width:69px;height:20px;font-size:12px;ime-mode:active;}#uchat_wrap47445045 .none {float:left;font-size:0;line-height:0;width:0;height:0;overflow:hidden}#uchat_wrap47445045 .uchat_menu {background:url("http://cache.uchat.co.kr/uchat/img/menu_background.gif");width:98px;height:25px;overflow:hidden}#uchat_wrap47445045 .uchat_menu .uchat_s_logo {float:left;width:27px;height:25px;background:url("http://cache.uchat.co.kr/uchat/img/uchat_small.gif") 50% 50% no-repeat;}#uchat_wrap47445045 .uchat_menu .uchat_bold {float:left;width:16px;height:25px;background:url("http://cache.uchat.co.kr/uchat/img/text-bold-icon.gif") 0 50% no-repeat;cursor:pointer;}#uchat_wrap47445045 .uchat_menu .uchat_i {float:left;width:16px;height:25px;background:url("http://cache.uchat.co.kr/uchat/img/text-italic-icon.gif") 0 50% no-repeat;cursor:pointer;}#uchat_wrap47445045 .uchat_menu .uchat_underline {float:left;width:16px;height:25px;background:url("http://cache.uchat.co.kr/uchat/img/text-underline-icon.gif") 0 50% no-repeat;cursor:pointer;}#uchat_wrap47445045 .uchat_menu .uchat_text_color {float:left;width:16px;height:25px;background:black url("http://cache.uchat.co.kr/uchat/img/uchat_text_color.gif");cursor:pointer;}#uchat_wrap47445045 .uchat_menu .uchat_event_icon {float:right;width:20px;height:25px;background-repeat: no-repeat;background-position: center center; background-color: transparent;cursor:pointer}#uchat_wrap47445045 .uchat_menu .uchat_scroll {float:right;width:16px;height:25px;background:url("http://cache.uchat.co.kr/uchat/img/uchat_scroll.gif");cursor:pointer;}#uchat_wrap47445045 .uchat_color_wrap {width:112px;border:1px solid #ccc;position:absolute;background:#f2f2f2;display:none;}#uchat_wrap47445045 .uchat_color_wrap .uchat_color_title {text-align:center;width:112px;padding:7px 0;border-bottom:1px solid #ccc;font-size:12px;}#uchat_wrap47445045 .uchat_color_wrap .uchat_color_box {width:112px;border-top:1px solid #fff;}#uchat_wrap47445045 .uchat_color_wrap .uchat_color_box:after {display:block;clear:both;content:""}#uchat_wrap47445045 .uchat_color_wrap .uchat_color {float:left;cursor:pointer;width:14px;height:14px;font-size:0;line-height:0;}#uchat_wrap47445045 .uchat_setting_wrap {width:150px;overflow:hidden;border:2px solid #a0dbc7;position:absolute;display:none;background:#fff;z-index:1100;}#uchat_wrap47445045 .uchat_setting_wrap .top {border-bottom:1px solid #cecece;text-align:center;padding:5px 0;font-size:14px;}#uchat_wrap47445045 .uchat_setting_wrap .top span {height:12px;}#uchat_wrap47445045 .uchat_setting_wrap .content {border-top:1px solid #f7f7f7;padding:5px;}#uchat_wrap47445045 .uchat_setting_wrap .floor{margin-bottom:7px;}#uchat_wrap47445045 .uchat_setting_wrap .floor input.no_sound{width:12px;height:12px;border:0;}#uchat_wrap47445045 .uchat_nick { width: 100%;}#uchat_wrap47445045 .alert_btn_wrap {text-align:right;margin:0 5px 10px 0;}#uchat_wrap47445045 .alert_ok_btn { }#uchat_wrap47445045 .alert_cancel_btn { }#uchat_wrap47445045 .clear {clear:both; height:0; overflow:hidden;}#uchat_wrap47445045 .popup {background:#fff;display:none;position:absolute;border:2px solid #a0dbc7;z-index:1100; margin: 0px auto;}#uchat_wrap47445045 .popup_background {position:absolute;left:0px;top:0;width:98px;height:548px;background:#e1e1e1;z-index:1000;display:none; filter: alpha(opacity=40);-khtml-opacity: 0.4;-moz-opacity: 0.4;opacity: 0.4; }#uchat_wrap47445045 .input_border {border-top:1px solid #9a9a9a !important;border-left:1px solid #9a9a9a !important;border-right:1px solid #d8d8d8 !important;border-bottom:1px solid #d8d8d8 !important;}#uchat_wrap47445045 .btn_pack, #uchat_wrap47445045 .btn_pack *{display:inline-block;overflow:visible;position:relative;margin:0;padding:0;border:0;background:url(http://cache.uchat.co.kr/uchat/img/btn_pack.gif) no-repeat;font-size:12px;font-family:Tahoma, Sans-serif;color:#333;text-decoration:none !important;vertical-align:top;white-space:nowrap}#uchat_wrap47445045 .btn_pack{margin-right:4px}#uchat_wrap47445045 .btn_pack *{left:4px;cursor:pointer;_cursor:hand}#uchat_wrap47445045 .btn_pack.medium,#uchat_wrap47445045 .btn_pack.medium *{height:24px;line-height:24px}#uchat_wrap47445045 .btn_pack.medium{background-position:left 0}#uchat_wrap47445045 .btn_pack.medium *{padding:0 10px 0 6px;background-position:right top;font-size:12px}#uchat_wrap47445045 .btn_pack *:hover, #uchat_wrap47445045 .btn_pack *:active, #uchat_wrap47445045 .btn_pack *:focus{color:#690}#uchat_wrap47445045 .btn_pack.strong *{font-weight:bold !important}#uchat_wrap47445045 .btn_pack.small, #uchat_wrap47445045 .btn_pack.small *{height:19px;line-height:19px}#uchat_wrap47445045 .btn_pack.small{background-position:left -106px}#uchat_wrap47445045 .btn_pack.small *{padding:0 6px 0 2px;background-position:right -106px;font-size:11px}#uchat_wrap47445045 input {background:#fff;vertical-align:middle;height:20px;}#uchat_wrap47445045 .ad_spot {overflow:hidden; position: absolute; top: 24px; width: 100%; max-width: 300px; z-index: 1;}#uchat_wrap47445045 .Bnr_icon {overflow:hidden; border-top:2px solid #d7d7d7;}#uchat_wrap47445045 .tb_button {padding:1px;cursor:pointer;border-right: 1px solid #8b8b8b;border-left: 1px solid #FFF;border-bottom: 1px solid #fff;}#uchat_wrap47445045 .tb_button.hover {borer:2px outset #def; background-color: #f8f8f8 !important;}#uchat_wrap47445045 .ws_toolbar {z-index:100000}#uchat_wrap47445045 .ws_toolbar .ws_tb_btn {cursor:pointer;border:1px solid #555;padding:3px}#uchat_wrap47445045 .tb_highlight{background-color:yellow}#uchat_wrap47445045 .tb_hide {visibility:hidden}#uchat_wrap47445045 .ws_toolbar img {padding:2px;margin:0px}</style> <style>#uchat_goodlin {width:98px;height:548px;position:relative;display:none;overflow:hidden;}#uchat_goodlin .uchat_middle .conversation_contents {width:100%;height:474px;}#uchat_goodlin .uchat_middle .member_list {width:100%;height:-1px;margin-bottom:1px;}</style> 이렇게 코드가 다닥다닥 붙어서 제대로 해석이 안되는 경우가 있는 것 같습니다.
?
geusgod 9년 전
@charset "utf-8"; /* Element Reset */ body,table,input,textarea,select,button{font-family:Tahoma,Geneva,sans-serif;font-size:12px} img{border:0} /* Button */ .btn{position:relative;display:inline-block;vertical-align:middle} .btn *{display:inline-block;padding:0 8px;font-size:12p… @charset "utf-8"; /* Element Reset */ body,table,input,textarea,select,button{font-family:Tahoma,Geneva,sans-serif;font-size:12px} img{border:0} /* Button */ .btn{position:relative;display:inline-block;vertical-align:middle} .btn *{display:inline-block;padding:0 8px;font-size:12px;height:24px;line-height:22px;margin:0;font-weight:bold !important;color:#fff;text-decoration:none !important;border:1px solid;cursor:pointer;overflow:visible;border-radius:3px;box-shadow:inset 0 0 1px #fff;background-color:#666;text-shadow:0 -1px 0 #333;zoom:1} .btn *[type=submit][disabled=disabled], .btn *[type=button][disabled=disabled]{opacity:.5;*filter:alpha(opacity=50)} .btn a, .btn button[type=button]{border-color:#ccc;color:#333 !important;background:#eee -webkit-gradient(linear,0% 0%,0% 100%,from(#fff),to(#ddd));background:#eee -moz-linear-gradient(top,#fff,#ddd);background-color:#eee;text-shadow:1px 1px 0 #fff;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#ffffff, endColorStr=#dddddd)} .btn input, .btn button[type=submit]{border-color:#666;background:#333 -webkit-gradient(linear,0% 0%,0% 100%,from(#777),to(#777),color-stop(0.5,#333),color-stop(0.5,#000)) !important;background:#333 -moz-linear-gradient(top,#777,#000) !important;background-color:#333 !important;color:#ffc !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#777777, endColorStr=#333333)} .btn a{height:22px} .btn.medium *{padding:0 12px;font-size:16px;height:30px;line-height:28px} .btn.medium a{height:28px} .btn.large *{padding:0 18px;font-size:22px;height:36px;line-height:34px} .btn.large a{height:34px} /* Button - Regucy */ span.button, a.button{position:relative;display:inline-block;vertical-align:top} span.button *, a.button *{display:inline-block;padding:0 8px;font-size:12px;height:24px;line-height:22px;margin:0;font-weight:bold !important;color:#fff;text-decoration:none !important;border:1px solid;cursor:pointer;overflow:visible;border-radius:3px;box-shadow:inset 0 0 1px #fff;background-color:#666;text-shadow:0 -1px 0 #333;zoom:1} span.button *[type=submit][disabled=disabled], span.button *[type=button][disabled=disabled]{opacity:.5;*filter:alpha(opacity=50)} a.button span, span.button button[type=button]{border-color:#ccc;color:#333 !important;background:#eee -webkit-gradient(linear,0% 0%,0% 100%,from(#fff),to(#ddd));background:#eee -moz-linear-gradient(top,#fff,#ddd);background-color:#eee;text-shadow:1px 1px 0 #fff;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#ffffff, endColorStr=#dddddd)} span.button input, span.button button[type=submit]{border-color:#666;background:#333 -webkit-gradient(linear,0% 0%,0% 100%,from(#777),to(#777),color-stop(0.5,#333),color-stop(0.5,#000));background:#333 -moz-linear-gradient(top,#777,#000);background-color:#333;color:#ffc !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#777777, endColorStr=#333333)} a.button span{height:22px} /* Button Area */ .btnArea{margin:1em 0;text-align:right;zoom:1} .btnArea:after{content:"";display:block;clear:both} .btnArea .etc{float:left} /* Text Button */ input[type=submit].text, input[type=button].text, button[type=submit].text, button[type=button].text{border:0;overflow:visible;padding:0;margin:0 4px 0 0;color:#33a !important;background:none;text-decoration:underline;cursor:pointer} /* Popup Menu Area */ #popup_menu_area{position:absolute;background:#fff;border:1px solid #e9e9e9;border-radius:5px;padding:10px;line-height:1.3;box-shadow:0 0 6px #666;font-size:12px;filter:progid:DXImageTransform.Microsoft.Shadow(color=#999999,direction=135, strength=5)} #popup_menu_area ul{list-style:none;margin:0;padding:0} #popup_menu_area li{margin:0;padding:0} #popup_menu_area a{text-decoration:none;color:#333} #popup_menu_area a:hover, #popup_menu_area a:avtive, #popup_menu_area a:focus{text-decoration:underline} /* Message */ .message{border:1px solid #ddd;background:#f8f8f8;margin:1em 0;padding:0 1em;border-radius:5px;line-height:1.4;font-size:12px} body>.message{margin:1em} .message p{margin:1em 0 !important} .message em{font-style:normal;color:#e00} .message.info, .message.error, .message.update{padding-left:55px} .message.info{border-color:#E0E8EC;background:#EDF9FF url(../../common/img/msg.Info.png) no-repeat 1em .5em} .message.error{border-color:#EFDCDC;background:#FFECEC url(../../common/img/msg.error.png) no-repeat 1em .5em} .message.update{border-color:#EAE9DC;background:#FFFDEF url(../../common/img/msg.update.png) no-repeat 1em .5em} /* Waiting for server response */ .wfsr{display:none;position:absolute;position:fixed;left:0;top:0;z-index:100; border:1px solid #EAE9DC;background:#FFFDEF url(../../common/img/msg.loading.gif) no-repeat 1em .5em;margin:1em;padding:1em 1em 1em 55px;border-radius:5px;line-height:1.4;font-size:12px;font-weight:bold} /* Waiting for server response - Modal Window */ .wfsr_fog{position:absolute;top:0;left:0;width:100%;_height:100%;min-height:100%;z-index:100} .wfsr_fog .bg{position:absolute;position:fixed;background:#000;_background:none;width:100%;height:100%;opacity:.5;z-index:2;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50);zoom:1} .wfsr_fog .ie6{position:absolute;left:0;top:0;width:100%;height:100%;border:0;opacity:0;filter:alpha(opacity=0);z-index:1} 아래는 몇군데 확인하고 넘어가야 부분들이다. /* Element Reset */ body,table,input,textarea,select,button{font-family:Tahoma,Geneva,sans-serif;font-size:12px} img{border:0} /* Text Button */ input[type=submit].text, input[type=button].text, button[type=submit].text, button[type=button].text{border:0;overflow:visible;padding:0;margin:0 4px 0 0;color:#33a !important;background:none;text-decoration:underline;cursor:pointer} /* Popup Menu Area */ #popup_menu_area{position:absolute;background:#fff;border:1px solid #e9e9e9;border-radius:5px;padding:10px;line-height:1.3;box-shadow:0 0 6px #666;font-size:12px;filter:progid:DXImageTransform.Microsoft.Shadow(color=#999999,direction=135, strength=5)} #popup_menu_area ul{list-style:none;margin:0;padding:0} #popup_menu_area li{margin:0;padding:0} #popup_menu_area a{text-decoration:none;color:#333} #popup_menu_area a:hover, #popup_menu_area a:avtive, #popup_menu_area a:focus{text-decoration:underline} /* Message */ .message{border:1px solid #ddd;background:#f8f8f8;margin:1em 0;padding:0 1em;border-radius:5px;line-height:1.4;font-size:12px} body>.message{margin:1em} .message p{margin:1em 0 !important} .message em{font-style:normal;color:#e00} .message.info, .message.error, .message.update{padding-left:55px} .message.info{border-color:#E0E8EC;background:#EDF9FF url(../../common/img/msg.Info.png) no-repeat 1em .5em} .message.error{border-color:#EFDCDC;background:#FFECEC url(../../common/img/msg.error.png) no-repeat 1em .5em} .message.update{border-color:#EAE9DC;background:#FFFDEF url(../../common/img/msg.update.png) no-repeat 1em .5em} /* Waiting for server response */ .wfsr{display:none;position:absolute;position:fixed;left:0;top:0;z-index:100; border:1px solid #EAE9DC;background:#FFFDEF url(../../common/img/msg.loading.gif) no-repeat 1em .5em;margin:1em;padding:1em 1em 1em 55px;border-radius:5px;line-height:1.4;font-size:12px;font-weight:bold} /* Waiting for server response - Modal Window */ .wfsr_fog{position:absolute;top:0;left:0;width:100%;_height:100%;min-height:100%;z-index:100} .wfsr_fog .bg{position:absolute;position:fixed;background:#000;_background:none;width:100%;height:100%;opacity:.5;z-index:2;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50);zoom:1} .wfsr_fog .ie6{position:absolute;left:0;top:0;width:100%;height:100%;border:0;opacity:0;filter:alpha(opacity=0);z-index:1}
?
클론 9년 전
@charset "utf-8"; /* Element Reset */ body,table,input,textarea,select,button{font-family:Tahoma,Geneva,sans-serif;font-size:12px} img{border:0} /* Button */ .btn{position:relative;display:inline-block;vertical-align:middle} .btn *{display:inline-block;padding:0 8px;font-size:12p… @charset "utf-8"; /* Element Reset */ body,table,input,textarea,select,button{font-family:Tahoma,Geneva,sans-serif;font-size:12px} img{border:0} /* Button */ .btn{position:relative;display:inline-block;vertical-align:middle} .btn *{display:inline-block;padding:0 8px;font-size:12px;height:24px;line-height:22px;margin:0;font-weight:bold !important;color:#fff;text-decoration:none !important;border:1px solid;cursor:pointer;overflow:visible;border-radius:3px;box-shadow:inset 0 0 1px #fff;background-color:#666;text-shadow:0 -1px 0 #333;zoom:1} .btn *[type=submit][disabled=disabled], .btn *[type=button][disabled=disabled]{opacity:.5;*filter:alpha(opacity=50)} .btn a, .btn button[type=button]{border-color:#ccc;color:#333 !important;background:#eee -webkit-gradient(linear,0% 0%,0% 100%,from(#fff),to(#ddd));background:#eee -moz-linear-gradient(top,#fff,#ddd);background-color:#eee;text-shadow:1px 1px 0 #fff;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#ffffff, endColorStr=#dddddd)} .btn input, .btn button[type=submit]{border-color:#666;background:#333 -webkit-gradient(linear,0% 0%,0% 100%,from(#777),to(#777),color-stop(0.5,#333),color-stop(0.5,#000)) !important;background:#333 -moz-linear-gradient(top,#777,#000) !important;background-color:#333 !important;color:#ffc !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#777777, endColorStr=#333333)} .btn a{height:22px} .btn.medium *{padding:0 12px;font-size:16px;height:30px;line-height:28px} .btn.medium a{height:28px} .btn.large *{padding:0 18px;font-size:22px;height:36px;line-height:34px} .btn.large a{height:34px} /* Button - Regucy */ span.button, a.button{position:relative;display:inline-block;vertical-align:top} span.button *, a.button *{display:inline-block;padding:0 8px;font-size:12px;height:24px;line-height:22px;margin:0;font-weight:bold !important;color:#fff;text-decoration:none !important;border:1px solid;cursor:pointer;overflow:visible;border-radius:3px;box-shadow:inset 0 0 1px #fff;background-color:#666;text-shadow:0 -1px 0 #333;zoom:1} span.button *[type=submit][disabled=disabled], span.button *[type=button][disabled=disabled]{opacity:.5;*filter:alpha(opacity=50)} a.button span, span.button button[type=button]{border-color:#ccc;color:#333 !important;background:#eee -webkit-gradient(linear,0% 0%,0% 100%,from(#fff),to(#ddd));background:#eee -moz-linear-gradient(top,#fff,#ddd);background-color:#eee;text-shadow:1px 1px 0 #fff;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#ffffff, endColorStr=#dddddd)} span.button input, span.button button[type=submit]{border-color:#666;background:#333 -webkit-gradient(linear,0% 0%,0% 100%,from(#777),to(#777),color-stop(0.5,#333),color-stop(0.5,#000));background:#333 -moz-linear-gradient(top,#777,#000);background-color:#333;color:#ffc !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#777777, endColorStr=#333333)} a.button span{height:22px} /* Button Area */ .btnArea{margin:1em 0;text-align:right;zoom:1} .btnArea:after{content:"";display:block;clear:both} .btnArea .etc{float:left} /* Text Button */ input[type=submit].text, input[type=button].text, button[type=submit].text, button[type=button].text{border:0;overflow:visible;padding:0;margin:0 4px 0 0;color:#33a !important;background:none;text-decoration:underline;cursor:pointer} /* Popup Menu Area */ #popup_menu_area{position:absolute;background:#fff;border:1px solid #e9e9e9;border-radius:5px;padding:10px;line-height:1.3;box-shadow:0 0 6px #666;font-size:12px;filter:progid:DXImageTransform.Microsoft.Shadow(color=#999999,direction=135, strength=5)} #popup_menu_area ul{list-style:none;margin:0;padding:0} #popup_menu_area li{margin:0;padding:0} #popup_menu_area a{text-decoration:none;color:#333} #popup_menu_area a:hover, #popup_menu_area a:avtive, #popup_menu_area a:focus{text-decoration:underline} /* Message */ .message{border:1px solid #ddd;background:#f8f8f8;margin:1em 0;padding:0 1em;border-radius:5px;line-height:1.4;font-size:12px} body>.message{margin:1em} .message p{margin:1em 0 !important} .message em{font-style:normal;color:#e00} .message.info, .message.error, .message.update{padding-left:55px} .message.info{border-color:#E0E8EC;background:#EDF9FF url(../../common/img/msg.Info.png) no-repeat 1em .5em} .message.error{border-color:#EFDCDC;background:#FFECEC url(../../common/img/msg.error.png) no-repeat 1em .5em} .message.update{border-color:#EAE9DC;background:#FFFDEF url(../../common/img/msg.update.png) no-repeat 1em .5em} /* Waiting for server response */ .wfsr{display:none;position:absolute;position:fixed;left:0;top:0;z-index:100; border:1px solid #EAE9DC;background:#FFFDEF url(../../common/img/msg.loading.gif) no-repeat 1em .5em;margin:1em;padding:1em 1em 1em 55px;border-radius:5px;line-height:1.4;font-size:12px;font-weight:bold} /* Waiting for server response - Modal Window */ .wfsr_fog{position:absolute;top:0;left:0;width:100%;_height:100%;min-height:100%;z-index:100} .wfsr_fog .bg{position:absolute;position:fixed;background:#000;_background:none;width:100%;height:100%;opacity:.5;z-index:2;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50);zoom:1} .wfsr_fog .ie6{position:absolute;left:0;top:0;width:100%;height:100%;border:0;opacity:0;filter:alpha(opacity=0);z-index:1} 아래는 몇군데 확인하고 넘어가야 부분들이다. /* Element Reset */ body,table,input,textarea,select,button{font-family:Tahoma,Geneva,sans-serif;font-size:12px} img{border:0} /* Text Button */ input[type=submit].text, input[type=button].text, button[type=submit].text, button[type=button].text{border:0;overflow:visible;padding:0;margin:0 4px 0 0;color:#33a !important;background:none;text-decoration:underline;cursor:pointer} /* Popup Menu Area */ #popup_menu_area{position:absolute;background:#fff;border:1px solid #e9e9e9;border-radius:5px;padding:10px;line-height:1.3;box-shadow:0 0 6px #666;font-size:12px;filter:progid:DXImageTransform.Microsoft.Shadow(color=#999999,direction=135, strength=5)} #popup_menu_area ul{list-style:none;margin:0;padding:0} #popup_menu_area li{margin:0;padding:0} #popup_menu_area a{text-decoration:none;color:#333} #popup_menu_area a:hover, #popup_menu_area a:avtive, #popup_menu_area a:focus{text-decoration:underline} /* Message */ .message{border:1px solid #ddd;background:#f8f8f8;margin:1em 0;padding:0 1em;border-radius:5px;line-height:1.4;font-size:12px} body>.message{margin:1em} .message p{margin:1em 0 !important} .message em{font-style:normal;color:#e00} .message.info, .message.error, .message.update{padding-left:55px} .message.info{border-color:#E0E8EC;background:#EDF9FF url(../../common/img/msg.Info.png) no-repeat 1em .5em} .message.error{border-color:#EFDCDC;background:#FFECEC url(../../common/img/msg.error.png) no-repeat 1em .5em} .message.update{border-color:#EAE9DC;background:#FFFDEF url(../../common/img/msg.update.png) no-repeat 1em .5em} /* Waiting for server response */ .wfsr{display:none;position:absolute;position:fixed;left:0;top:0;z-index:100; border:1px solid #EAE9DC;background:#FFFDEF url(../../common/img/msg.loading.gif) no-repeat 1em .5em;margin:1em;padding:1em 1em 1em 55px;border-radius:5px;line-height:1.4;font-size:12px;font-weight:bold} /* Waiting for server response - Modal Window */ .wfsr_fog{position:absolute;top:0;left:0;width:100%;_height:100%;min-height:100%;z-index:100} .wfsr_fog .bg{position:absolute;position:fixed;background:#000;_background:none;width:100%;height:100%;opacity:.5;z-index:2;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50);zoom:1} .wfsr_fog .ie6{position:absolute;left:0;top:0;width:100%;height:100%;border:0;opacity:0;filter:alpha(opacity=0);z-index:1}
이온디
이온디 9년 전
Posted at 2009/02/12 11:32 [ by Lovelet ] 1. 관련링크1를 참조하여 스마트에디터를 다운로드 합니다. 2. 압축을 해제한 후 홈페이지 계정/bbs/SmartEditor로 업로드 합니다. 3. 적용하고자 하시는 스킨의 write.php파일을 에디터로 여세요. 4. 가장 윗부분에 아래의 소스를 붙여 넣으세요. <script type="text/javascrip… Posted at 2009/02/12 11:32 [ by Lovelet ] 1. 관련링크1를 참조하여 스마트에디터를 다운로드 합니다. 2. 압축을 해제한 후 홈페이지 계정/bbs/SmartEditor로 업로드 합니다. 3. 적용하고자 하시는 스킨의 write.php파일을 에디터로 여세요. 4. 가장 윗부분에 아래의 소스를 붙여 넣으세요. <script type="text/javascript" src="SmartEditor/js/HuskyEZCreator.js"></script> 5. 아래의 소스를 찾으셔서 value값을 수정하세요. <?=$hide_html_start?><input type=checkbox name=use_html checked <?=$use_html?> value=2>HTML 사용<?=$hide_html_end?> 6. 기본 스킨 기준으로 아래의 소스를 수정하세요. <textarea name=memo <?=size2(90)?> rows=18 class=textarea style=width:99%><?=$memo?></textarea> <textarea name=memo id="ir1" <?=size2(90)?> rows=18 class=textarea style='width:99%;'><?=$memo?></textarea> 7. 작성완료 부분을 아래처럼 이벤트를 추가해주세요. <input type=image src=<?=$dir?>/submit.gif accesskey="s" onfocus='this.blur()' alt=확인 onClick="_onSubmit(this);"> 8. 제일 하단에 아래의 소스를 추가합니다. (진한 부분의 소스는 클립보드에 복사하는 소스로 필요없으시면 삭제하시면 됩니다.) <script> var oEditors = []; nhn.husky.EZCreator.createInIFrame(oEditors, "ir1", "SmartEditor/SEditorSkin.html", "createSEditorInIFrame"); // var oEditors = []; // 마지막 옵션은 체감 속도 증진을 위해서 페이지 로딩 완료시 까지 화면 표시를 하지 않는 옵션 입니다. // 개발 작업시에는 이 값을 false로 설정 하세요. // nhn.husky.EZCreator.createInIFrame(oEditors, "ir1", "SmartEditor/SEditorSkin.html", "createSEditorInIFrame", null, true); // 복수개의 에디터를 생성하고자 할 경우, 아래와 같은 방식으로 호출하고 oEditors.getById["ir2"]이나 oEditors[1]을 이용해 접근하면 됩니다. // nhn.husky.EZCreator.createInIFrame(oEditors, "ir2", "SEditorSkin.html", "createSEditorInIFrame", null, true); function pasteHTMLDemo(){ sHTML = "<span style='color:#FF0000'>이미지 등도 이렇게 삽입하면 됩니다.</span>"; oEditors.getById["memo"].exec("PASTE_HTML", [sHTML]); } function showHTML(){ alert(oEditors.getById["memo"].getIR()); } function _onSubmit(elClicked){ // 에디터의 내용을 에디터 생성시에 사용했던 textarea에 넣어 줍니다. oEditors.getById["ir1"].exec("UPDATE_IR_FIELD", []); <? if(!$member[no]) { ?> if(!document.getElementById("name").value) {return false;} if(!document.getElementById("password").value) {return false;} <? } ?> <? if($setup[use_category]) { ?> var myindex=document.write.category[1].selectedIndex; if (myindex<1) { alert('카테고리를 선택해 주세요.'); return false; } <? } ?> if(!document.getElementById("subject").value) {return false;} if(!document.getElementById("ir1").value) {alert('내용을 입력해 주세요.');return false;} var buf = document.getElementById("ir1"); var Range = buf.createTextRange (); Range.execCommand ("Copy"); alert("작성하신 글이 클립보드에 복사되었습니다."); // 에디터의 내용에 대한 값 검증은 이곳에서 document.getElementById("ir1").value를 이용해서 처리하면 됩니다. try{ elClicked.form.submit(); }catch(e){} } </script> 9. 스마트에디터를 잘 적용하기위해서 스타일을 적용시켜주어야 합니다. 스킨의 setup.php를 열고 아래와 같이 수정합니다. (또는 style.css파일에 해당 항목만 추가를 하셔도 됩니다. 아래의 팁은 해당 style.css파일만 수정하는 방법입니다.) .smartOutput{ font-size:12px; line-height:1.6; font-family:굴림, AppleGothic, Sans-serif;} /* 스마트 에디터의 풍부한 표현이 정상적으로 출력되도록 하려면 콘텐츠가 출력되는 곳에 이 클래스를 적용하여야 한다. 예를 들면 게시물 읽기 페이지의 본문이 이에 해당된다. */ .smartOutput p{ margin-top:7px; margin-bottom:7px;} .smartOutput blockquote.q1, .smartOutput blockquote.q2, .smartOutput blockquote.q3, .smartOutput blockquote.q4, .smartOutput blockquote.q5, .smartOutput blockquote.q6, .smartOutput blockquote.q7{ padding:10px; margin-left:15px; margin-right:15px;} .smartOutput blockquote.q1{ padding:0 10px; border-left:2px solid #ccc;} .smartOutput blockquote.q2{ padding:0 10px; background:url(../img/bg_qmark.gif) no-repeat;} .smartOutput blockquote.q3{ border:1px solid #d9d9d9;} .smartOutput blockquote.q4{ border:1px solid #d9d9d9; background:#fbfbfb;} .smartOutput blockquote.q5{ border:2px solid #707070;} .smartOutput blockquote.q6{ border:1px dashed #707070;} .smartOutput blockquote.q7{ border:1px dashed #707070; background:#fbfbfb;} .smartOutput sup{ font:10px Tahoma;} .smartOutput sub{ font:10px Tahoma;} .smartOutput table td{ padding:4px;} 이 소스를 style.css파일에 붙여넣습니다. 또는 setup.php파일(또는 view.php파일)에서 <link rel="stylesheet" type="text/css" href="../../SmartEditor/css/style.css" /> 이런 식으로 해주시면 되겠죠. 10. 그리고나서 스킨의 view.php파일을 열고, <?=$memo?>를 찾아줍니다. 아마 이 변수는 테이블에 둘러쌓여 있는데, 여기에 클래스를 지정해주시면 됩니다. <td><?=$memo?></td>인 경우 <td class="smartOutput"><?=$memo?></td> 11. 잘 적용이 되었는지 테스트해보시기 바랍니다. 기타 문의사항은 Web Q&A에 올려주시기 바랍니다. 진도 프레임웍은 이 곳에서 다운로드 : http://dev.naver.com/projects/jindo/download 태그가 적용이 안되신다면, 게시판 설정에서 html 사용권한의 레벨을 확인해보세요~ http://dev.naver.com/projects/smarteditor/download 인쇄하기 덧글(2) Commented by epikfan.co at 2009-07-29 08:50:32 감사합니다^6 다른님들 거 보면 계속 내용입력하라그러는데 작성완료가 문제였군요^^ 아무튼 감사합니다^6 IP 115.161.76.XXX Commented by fuzzionkai at 2009-10-30 12:44:50 안녕하세요. 올려주신 방법 보고 잘 올렸는데요. 이미지박스는 삽입이 안되요. --; 하하. 좀더 공부해야겠어요
이온디
이온디 9년 전
1. 어려운점은 많았지만 첫번째 조건은 '반응형웹'이었습니다. 이에 따른 문제점이 많았죠. 2. 데스크탑의 코드와 모바일에서의 코드가 같아야 한다는 점. 3. 부트스트랩 그리드 시스템에서의 패딩값 처리 4. 보더값 처리 문제 5. 테이블로 할까 vs UL, LI 태그로 해야할까 6. 기획서를 보면 더보기 버튼을 누르면 처음엔 두줄씩 보여지지만, 클릭시 2줄이 추가가 되는 구조입니다. 이를 고려해서 패딩값과 보더 처리를 해야합니다. 삽질은 기본이었죠. #삽질의 과정 시작 삽질1) 테이블로도 짜… 1. 어려운점은 많았지만 첫번째 조건은 '반응형웹'이었습니다. 이에 따른 문제점이 많았죠. 2. 데스크탑의 코드와 모바일에서의 코드가 같아야 한다는 점. 3. 부트스트랩 그리드 시스템에서의 패딩값 처리 4. 보더값 처리 문제 5. 테이블로 할까 vs UL, LI 태그로 해야할까 6. 기획서를 보면 더보기 버튼을 누르면 처음엔 두줄씩 보여지지만, 클릭시 2줄이 추가가 되는 구조입니다. 이를 고려해서 패딩값과 보더 처리를 해야합니다. 삽질은 기본이었죠. #삽질의 과정 시작 삽질1) 테이블로도 짜보고 <!-- table <div class="rank-product"> <table cellpadding="0" cellspacing="0"> <tr> <td> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </td> <td> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </td> <td> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </td> <td> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </td> </tr> </table></div>-->삽질2) 테이블일 경우, LI 일 경우 테스트 /* li일 경우.rank-product {padding:0 60px;display:table;box-sizing:border-box;}.rank-product li{text-align:center;border:1px solid #ccc;border-left:0;padding:30px 0;left:-1px;float:left;display:table-cell;}.rank-product li:first-child{border-left:1px solid #ccc;}.rank-product a{display:block;height:100%;}*//* table 일 경우.rank-product {padding:30px 60px;}.rank-product table {border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;width:100%;}.rank-product tr{border-top:1px solid #ccc;}.rank-product td{border-left:1px solid #ccc;text-align:center;}.rank-product td a{display:block;padding:30px 0;}*/#삽질의 과정 끝 해결과정1) 보더값과 항목이 추가될 경우를 모두 고려해야 하고, 반응형이므로 모바일에서의 항목을 보여주는 방식이 달라지므로 테이블로 코드를 짜는 것은 알맞지 않았습니다. UL, LI로 짜고, col-md-*, col-xs-* 부트스트랩 그리드 시스템을 사용해서 해결하기로 합니다. 해결과정2) 테이블로 코딩할 경우, 좀 더 쉽게 보더값을 줄 수 있지만, li로 할 경우 보더값을 주는 게 좀 까다로웠습니다. 1) ul에서 아래선, 오른쪽선을 지정합니다. 2) li에서 윗선, 왼쪽선을 지정합니다. 포지션값 relative로 지정하고, 왼쪽으로 1픽셀 이동합니다. 해결과정3) 부트스트랩 그리드&반응형이므로 padding값 조정이 어려운 점은 그냥 media 쿼리를 사용해서 처리하는 걸로 넘어갑니다. <style> .rank-product{padding:0 60px;} .rank-product ul{border-bottom:1px solid #ccc;border-right:1px solid #ccc;margin:0;padding:0;overflow:hidden;border-left:1px solid #ccc;} .rank-product li{border-top:1px solid #ccc;border-left:1px solid #ccc;position:relative;left:-1px;text-align:center;padding:20px 0;} @media (max-width: 767px) { .rank-product{padding:0;} }</style><div class="rank-product"> <ul class=" col-md-12 col-xd-12"> <li class="col-md-3 col-xs-6"> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </li> <li class="col-md-3 col-xs-6"> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </li> <li class="col-md-3 col-xs-6"> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </li> <li class="col-md-3 col-xs-6"> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </li> <!-- 새로 추가될 경우 --> <li class="col-md-3 col-xs-6"> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </li> <li class="col-md-3 col-xs-6"> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </li> <li class="col-md-3 col-xs-6"> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </li> <li class="col-md-3 col-xs-6"> <a href="#"> <div class="thumb"><img src="/asset/images/usr/thumb/rank-thumb.png" alt="thumb"></div> <div class="thumb-title">비타민 C</div> </a> </li> </ul></div>.. 작업 중이지만 전체 코드를 첨부합니다. sample-responsive-bootstrap-ul.zip
이온디
이온디 10년 전
한국의 전통 색상표 출처 : http://study4you.kr/xe/pds/752 무채색계(無彩色界) - 10색 흑백 1D1E23 93,89,83,52 백색 FFFFFF 0,0,0,0 회색 A4AAA7 38,27,31,… 한국의 전통 색상표 출처 : http://study4you.kr/xe/pds/752 무채색계(無彩色界) - 10색 흑백 1D1E23 93,89,83,52 백색 FFFFFF 0,0,0,0 회색 A4AAA7 38,27,31,0 구색 959EA2 45,32,32,0 치색 616264 72,64,62,4 연지회색 6F606E 55,58,40,20 설백색 DDE7E7 12,4,7,0 유배색 E7E6D2 9,5,18,0 지배색 E3DDCB 6,6,17,4 소색 D8C8B2 10,15,26,5 적색계(赤色界) - 21색 적색 B82647 21,98,68,8 홍색 F15B5B 0,80,60,0 적토색 9F494C 29,80,64,17 휴색 683235 40,80,66,44 갈색 966147 31,61,73,21 호박색 BD7F41 21,51,84,8 추향색 C38866 19,48,61,6 육색 D77964 11,62,59,2 주색 CA5E59 15,75,62,4 주홍색 C23352 18,94,60,5 담주색 EA8474 4,59,50,0 진홍색 BF2F7B 20,94,17,4 선홍색 CE5A9E 16,79,2,0 연지색 BE577B 19,77,28,7 훈색 D97793 9,64,20,2 진분홍색 DB4E9C 9,84,0,0 분홍색 E2A6B4 7,39,14,1 연분홍색 E0709B 6,69,11,1 장단색 E16350 6,75,70,1 석간주색 8A4C44 30,71,65,30 흑홍색 8E6F80 40,54,31,15 청록색계(靑綠色界) - 32색 청색 0B6DB7 89,56,0,0 벽색 00B5E3 73,5,4,0 천청색 5AC6D0 59,0,20,0 담청색 00A6A9 96,4,40,0 취람색 5DC19B 62,0,51,0 양람색 6C71B5 64,58,0,0 벽청색 448CCB 72,36,0,0 청현색 006494 99,59,22,3 감색 026892 93,57,26,2 남색 6A5BA8 68,73,0,0 연람색 7963AB 60,69,0,0 벽람색 6979BB 64,52,0,0 숙람색 45436C 86,84,40,9 군청색 4F599F 80,73,6,0 녹색 417141 82,44,95,9 명록색 16AA52 81,5,94,0 유록색 6AB048 64,8,97,0 유청색 569A49 72,20,96,1 연두색 C0D84D 29,0,87,0 춘유록색 CBDD61 24,0,78,0 청록색 009770 97,15,74,0 진초록색 0A8D5E 87,26,82,1 초록색 1C9249 85,20,98,2 흑록색 2E674E 89,52,83,9 비색 72C6A5 55,0,45,0 옥색 9ED6C0 38,0,30,0 삼청색 5C6EB4 71,59,0,0 뇌록색 397664 74,27,59,6 양록색 31B675 74,0,74,0 하염색 245441 83,43,75,39 흑청색 1583AF 84,39,17,0 청벽색 18B4E9 69,8,0,0 황색계(黃色界) - 16색 황색 F9D537 3,13,89,0 유황색 EBBC6B 6,25,67,1 명황색 FEE134 2,7,89,0 담황색 F5F0C5 4,2,27,0 송화색 F8E77F 4,4,62,0 자황색 F7B938 2,29,89,0 행황색 F1A55A 3,40,73,0 두록색 E5B98F 8,27,45,1 적황색 ED9149 4,51,80,0 토황색 C8852C 18,50,97,5 지황색 D6B038 14,26,91,3 토색 9A6B31 30,54,91,20 치자색 F6CF7A 3,18,61,0 홍황색 DDA28F 9,39,38,2 자황색 BB9E8B 22,33,40,7 금색 코드값이 없습니다. 자색계(紫色界) - 11색 자색 6D1B43 41,95,45,40 자주색 89236A 40,96,18,20 보라색 9C4998 42,85,1,1 홍람색 733E7F 58,85,10,15 포도색 5D3462 70,90,35,20 청자색 403F95 90,90,1,1 벽자색 84A7D3 47,25,1,1 회보라색 B3A7CD 28,32,1,1 담자색 BEA3C9 23,36,1,1 다자색 47302E 75,86,85,35 적자색 BA4160 15,86,42,13
이온디
이온디 10년 전
http://www.smoothdivscroll.com/ http://www.smoothdivscroll.com/basicDemo.html 왼쪽으로 흘러가는 텍스트 스크립트입니다. jquery와 css 를 사용해서 매우 부드럽게 흘러갑니다. 한페이지 내에서 1개 흘러가는 용도로 매우 유용하게 사용할 수 있습니다. 2개이상은 여러분의 몫입니다. 소스는 다음과 같으며, 내용중에 필요한 js와 css 그리고 image 는 첨부파일을 확인하세요. … http://www.smoothdivscroll.com/ http://www.smoothdivscroll.com/basicDemo.html 왼쪽으로 흘러가는 텍스트 스크립트입니다. jquery와 css 를 사용해서 매우 부드럽게 흘러갑니다. 한페이지 내에서 1개 흘러가는 용도로 매우 유용하게 사용할 수 있습니다. 2개이상은 여러분의 몫입니다. 소스는 다음과 같으며, 내용중에 필요한 js와 css 그리고 image 는 첨부파일을 확인하세요. <!doctype html> <html> <head> <meta charset="utf-8" /> <title>Scrolling Text - jQuery Smooth Div Scroll - Thomas Kahn</title> <!-- the CSS for Smooth Div Scroll --> <link rel="Stylesheet" type="text/css" href="Scripts/smoothDivScroll/smoothDivScroll.css" /> <style type="text/css"> /* text 스크롤 */ #scrollingText { width: 728px; height: 70px; position: relative; border: solid 1px #ccc; padding-top: 20px; } #scrollingText div.scrollableArea p { display: block; float: left; margin: 0; padding-right: 20px; font-family: Georgia,Times, 'Times New Roman', Serif; font-size: 45px; line-height: 45px; font-weight: bold; background-color: #fff; color: #000; white-space: nowrap; } #scrollingText2 div.scrollableArea2 p { display: block; float: left; margin: 0; padding-right: 20px; font-family: Georgia,Times, 'Times New Roman', Serif; font-size: 45px; line-height: 45px; font-weight: bold; background-color: #fff; color: #000; white-space: nowrap; } /* image 스크롤 */ #makeMeScrollable { width:470px; /* 보여지는 넓이 */ height: 330px; /* 보여지는 높이 */ position: relative; } #makeMeScrollable div.scrollableArea * { position: relative; display: block; float: left; margin: 0; padding: 0; /* If you don't want the images in the scroller to be selectable, try the following block of code. It's just a nice feature that prevent the images from accidentally becoming selected/inverted when the user interacts with the scroller. */ -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -o-user-select: none; user-select: none; } </style> </head> <body> <div id="scrollingText"> <p>You know, I have one simple request.</p> <p>And that is to have sharks with frickin' laser beams attached to their heads! </p> <p>Now evidently, my cycloptic colleague informs me that that can't be done. </p> <p>Can you remind me what I pay you people for? </p> <p>Honestly, throw me a bone here. What do we have? </p> </div> <div id="makeMeScrollable"> <img src="images/demo/field.jpg" alt="Field" id="field" /> <img src="images/demo/gnome.jpg" alt="Gnome" id="gnome" /> <img src="images/demo/pencils.jpg" alt="Pencils" id="pencils" /> <img src="images/demo/golf.jpg" alt="Golf" id="golf" /> <img src="images/demo/river.jpg" alt="River" id="river" /> <img src="images/demo/train.jpg" alt="Train" id="train" /> <img src="images/demo/leaf.jpg" alt="Leaf" id="leaf" /> <img src="images/demo/dog.jpg" alt="Dog" id="dog" /> </div> <!-- jQuery library - Please load it from Google API's --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js" type="text/javascript"></script> <!-- jQuery UI Widget and Effects Core (custom download) You can make your own at: http://jqueryui.com/download --> <script src="Scripts/smoothDivScroll/jquery-ui-1.8.23.custom.min.js" type="text/javascript"></script> <!-- Latest version of jQuery Mouse Wheel by Brandon Aaron You will find it here: http://brandonaaron.net/code/mousewheel/demos --> <script src="Scripts/smoothDivScroll/jquery.mousewheel.min.js" type="text/javascript"></script> <!-- jQuery Kinetic - for touch --> <script src="Scripts/smoothDivScroll/jquery.kinetic.js" type="text/javascript"></script> <!-- Smooth Div Scroll 1.3 minified--> <script src="Scripts/smoothDivScroll/jquery.smoothdivscroll-1.3-min.js" type="text/javascript"></script> <!-- If you want to look at the uncompressed version you find it at js/jquery.smoothDivScroll-1.3.js --> <!-- Plugin initialization --> <script type="text/javascript"> <!-- // text 스크롤 $(document).ready(function() { $("#scrollingText").smoothDivScroll({ autoScrollingMode: "always", autoScrollDirection: "endlessLoopRight", autoScrollInterval: 10, autoScrollStep: 2 }); } ); // image 스크롤 // Initialize the plugin with no custom options $(document).ready(function () { // I just set some of the options $("#makeMeScrollable").smoothDivScroll({ mousewheelScrolling: "allDirections", manualContinuousScrolling: true, autoScrollingMode: "onStart" }); }); //--> </script> </body> </html>
이온디
이온디 10년 전
HTML에 JavaScript 삽입 <script language="javascript"> JavaScript Statements </script> HTML에 JavaScript 코드 파일 삽입 <script language="javascipt" src="src.js"> </script> 변수의 대소문자 구분 변수에 포함된 데이터의 형을 지정하지 않고, JavaScript 인터프리터가 변수에 포함된 데이터 형을 추적하고 변환. JavaScript 데이터 형… HTML에 JavaScript 삽입 <script language="javascript"> JavaScript Statements </script> HTML에 JavaScript 코드 파일 삽입 <script language="javascipt" src="src.js"> </script> 변수의 대소문자 구분 변수에 포함된 데이터의 형을 지정하지 않고, JavaScript 인터프리터가 변수에 포함된 데이터 형을 추적하고 변환. JavaScript 데이터 형 숫 자 형 : 정수와 부동 소수 BOOLEAN : true, false STRING : 작은 따옴표나 큰 따옴표에 들어가 있는 값으로, 특수문자 포맷 사용시에는 '\'와 함께 사용. null : 아무런 값도 없는 것으로, 변수를 초기화 시키거나 어떤 값이나 이벤트를 지울 때 사용됨 undefined : 변수만 만들고, 값을 할당하지 않은 상태 유형간의 변환 스트링 피연산자가 비스트링 연산자와 사용된 경우 다른 연산자를 모두 스트링으로 변환 BOOLEAN값은 1과 0으로 변환되어 수치 연산을 지원 null값은 스트링 연산에 대해서는 "null", 논리 연산에서는 false, 그리고 수치 연산에서는 0으로 각각 변환 변환 함수 eval() : 스트링 표현식을 수치값으로 변환하고, 파라미터가 수치형태가 아닌 스트링 값이면 에러 발생 parseInt() : 스트링에 포함된 첫번째 정수를 리턴하고, 스트링이 정수로 시작되지 않으면 0을 리턴 parseFloat() : 스트링에 포함된 첫번째 부동 소수를 리턴하거나 스트링이 적절한 부동 소수로 시작되지 않으면 0을 리턴 배열 : 값의 시퀀스를 정렬할 수 있는 객체로, JavaScript의 특수형으로 배열 사용 전엔 반드시 선언해야 한다. 배열 선언 예 arrayName = new Array(arrayLength) arrayName = new Array() arrayName = new Array(value0,value1, ..., valuen) JavaScript만의 특수 연산자 comma(,) 연산자 : 두 표현식을 계산하고, 두 번째 표현식의 값 리턴 delete 연산자 : 객체의 프로퍼티나 배열 인덱스의 요소 삭제하고, 항상 undefined 값 리턴 new 연산자 : 객체 형의 인스턴스를 만들기 위해 사용 typeof 연산자 : 연산자의 형을 식별하는 스트링 값을 리턴 void 연산자 : 값을 리턴하지 않는다 지역 변수와 전역 변수 : 함수 안에서만 사용되는 것을 지역 변수라 하고, 프로그램 내에서 사용되는 변수를 전역 변수라 하는 데, 지역 변수 사용시 반드시 var 키워드와 함께 선언해야 함 이벤트 처리!! 이벤트 정의와 사용 이벤트 : 사용자가 웹페이지나 기타 다른 브라우저에 수행한 작업으로 인한 결과 이벤트 처리 : 이벤트로 인해 수행되는 프로세스 이벤트 핸들러 : 프로세스를 수행하는 코드 사용 예 : 사용자가 링크 위로 마우스를 갖다 대면 다이얼로그 박스를 표시한다거나, 폼에 입력한 데이터를 검증한다거나, 버튼을 클릭할 때 애니메이션을 나타내거나, Java 애플릿과 브라우저 플러그인이 상호작용을 하도록 한다. HTML 태그 JavaScript 이벤트 설명 다양 mouseMove 마우스 이동 <A>..</A> Click 마우스로 링크를 클릭 dbClick 마우스를 링크위에서 더블클릭 mouseDown 마우스 버튼을 누름 mouseUp 마우스 버튼을 놓음 mouseOver 마우스를 링크위로 이동 mouseOut 링크 위에 있던 마우스를 링크 밖으로 이동 keyDown 사용자가 키를 누름 keyUp 사용자가 키를 놓음 keyPress 사용자가 키를 눌렀다가 놓음 <IMG> abort 사용자 액션으로 인해 이미지 로딩 작업을 중단함 error 이미지 로딩하는 동안 에러 발생 load 이미지가 로드되고 화면에 나타남 keyDown 사용자가 키를 누름 keyUp 사용자가 키를 놓음 keyPress 사용자가 키를 눌렀다가 놓음 <AREA> mouseOver 마우스가 클라이언트측 이미지맵의 한 영역으로 이동함 mouseOut 마우스가 이미지맵 영역 밖으로 이동 dbClick 사용자가 이미지맵의 한 영역을 더블클릭함 <BODY>..</BODY> Click 사용자가 문서의 본문을 클릭 dbClick 문서의 본문을 더블 클릭함 keyDown 키를 누름 keyUp 키를 놓음 keyPress 키를 눌렀다가 놓음 mouseDown 마우스 버튼을 누름 mouseUp 마우스 버튼을 놓음 <BODY>..</BODY> <FRAMESET>..</FRAMESET> <FRAME>..</FRAME> blur 윈도우에서 현재 입력 포커스가 사라짐 error 윈도우가 로드되는 동안 에러 발생 focus 입력 포커스가 현재 윈도우로 이동 load 윈도우 로딩이 완료됨 unload 윈도우를 종료함 move 윈도우가 이동됨 resize 윈도우의 크기가 바뀜 dragDrop 윈도우에 객체를 드롭 <FORM>..</FORM> submit 사용자가 폼을 제출 reset 사용자가 폼을 재설정 <INPUT TYPE="text"> blur 현재 입력 포커스가 텍스트 필드에서 사라짐 focus 현재 입력 포커스가 텍스트 필드로 이동 change 텍스트 필드가 변경되어 현재 입력 포커스가 사라짐 select 텍스트 필드에 있는 텍스트가 선택됨 <INPUT TYPE="password"> blur 패스워드 필드에서 입력 포커스가 사라짐 focus 패스워드 필드에 입력 포커스 생김 <TEXTAREA>..</TEXTAREA> blur 텍스트 영역이 현재 입력 포커스가 사람짐 focus 텍스트 영역에 입력 포커스 생김 change 텍스트 영역이 변경되어 입력 포커스가 사라짐 select 텍스트 영역에서 텍스트가 선택됨 keyDown 키를 누름 keyUp 키를 놓음 keyPress 키를 눌렀다 놓음 <INPUT TYPE="button"> Click 버튼이 클릭됨 blur 입력할 수 없도록 버튼이 흐려짐 focus 입력할 수 있도록 포커스 생김 mouseDown 버튼 위에서 왼쪽 마우스 버튼 누름 mouseUp 버튼 위에서 왼쪽 마우스 버튼 놓음 <INPUT TYPE="submit"> Click 제출 버튼이 클릭됨 blur 제출 버튼에서 입력 포커스가 사라짐 focus 제출 버튼에 입력 포커스 생김 <INPUT TYPE="reset"> Click 리셋 버튼이 클릭됨 blur 리셋 버튼에서 포커스가 사라짐 focus 리셋 버튼에서 포터스가 놓임 <INPUT TYPE="radio"> Click 라디오 버튼이 클릭 blur 라디오 버튼에서 입력 포커스가 사라짐 focus 라디오 버튼에 입력 포커스 생김 <INPUT TYPE="checkbox"> Click 체크 박스가 클릭 blur 체크 박스에서 입력 포커스가 사라짐 focus 체크 박스에 입력 포커스 놓임 <INPUT TYPE="file"> blur 파일 업로드 폼 요소에서 입력 포커스 사라짐 change 사용자가 업로드될 파일을 선택 focus 파일 업로드 폼 요소에 입력 포커스 놓임 <SELECT>..</SELECT> blur 선택 요소가 현재 입력 포커스 잃음 change 선택 요소가 변경되어 입력 포커스가 사라짐 focus 선택 요소에 현재 입력 포커스가 놓임 이벤트 처리 속성 이벤트핸들링속성 실행되는 상황 onAbort 이미지를 로딩하는 작업이 사용자의 한 행동으로 인해 취소되었음 onBlur 문서나 윈도우, 프레임세트, 폼 요소에서 현재 입력 포커스가 사라짐 onChange 텍스트 필드나 텍스트 영역, 파일 업로드 필드, 선택 항목이 변경되어 현재 입력 포커스가 사라짐 onClick 링크나 클라이언트측 이미지맵 영역, 폼 요소가 클릭됨 onDbClick 링크나 클라이언트측 이미지맵 영역, 문서가 더블 클릭됨 onDragDrop 드래그된 객체가 윈도우나 프레임에 드롭됨 onError 이미지나 윈도우, 프레임을 로딩하는 동안 에러가 발생함 onFocus 문서나 윈도우, 프레임 세트, 폼 요소에 입력 포커스 놓임 onKeyDown 키를 누름 onKeyPress 키를 눌렀다 놓음 onKeyUp 키를 놓음 onLoad 이미지나 문서, 프레임이 로드됨 onMouseDown 마우스 버튼 누름 onMouseMove 마우스를 이동함 onMouseOut 링크나 클라이언트측 이미지맵에서 마우스를 옮김 onMouseOver 마우스를 링크나 클라이언트측 이미지맵으로 옮김 onMouseUp 마우스 버튼을 놓음 onMove 사용자가 윈도우나 프레임을 이동함 onReset 폼의 리셋 버튼을 클릭하여 폼을 리셋시킴 onResize 사용자가 윈도우나 프레임의 크기를 조절 onSelect 텍스트는 텍스트 필드나 영역에서 선택됨 onSubmit 폼이 제출됨 onUnload 사용자가 문서나 프레임 세트를 종료함 객체 정의하기 객체 유형 정의 프로퍼티 : 객체에 들어 있는 데이터 값에 액세스할 때 사용. 메소드 : 객체에 어떤 작업을 할 때 사용하는 함수. 객체 유형 만들기 사용자가 직접 객체 유형을 정의하고 특정 객체 인스턴스를 만들 수 있 는데 이렇게 만들려면 객체 유형의 특정 인스턴스를 만들 때 사용되는 함수를 정의하기만 하면 된다. 본래 이러한 생성자 함수는 다음과 같은 일을 한다. 객체 유형의 프로퍼티에 값을 할당한다. 객체 유형의 메소드로 사용할 수 있는 다른 함수를 지정한다. 객체 사용 예 table 객체의 정의 (table.js) function table_getValue(row,col){ return this.data[row* this.columns+col ]; } function table_setValue(row,col,value){ this.data[row* this.columns+col ]=value; } function table_set(contents){ var n=contents.length; for(var j=0;jthis.data[j]=contents[j]; } function table_isHeader(row,col){ return this.header[row* this.columns+col ]; } function table_makeHeader(row,col){ this.header[row* this.columns+col ]=true; } function table_makeNormal(row,col){ this.header[row* this.columns+col ]=false; } function table_makeHeaderRow(row){ for(var j=0;j< this.columns+j) this.header[row* this.columns+col ]=true; } function table_makeHeaderColumn(col){ for(var i=0;i< this.rows;++i) this.header[i* this.columns+col ]=true; } function table_write(doc){ doc.write("<TABLE BORDER="+ this.border+">"); for(var i=0;i< this.rows;++i) { doc.write("<TR>"); for(var j=0;j< this.columns;++j) { if( this.header[i* this.columns+j ]) { doc.write("<TH>"); doc.write( this.data[i* this.columns+j ]); doc.write("</TH>"); } else { doc.write("<TD>"); doc.write( this.data[i* this.columns+j ]); doc.write("</TD>"); } } doc.writeln("</TR>"); } doc.writeln("</TABLE>"); } funtion table(rows,columns) { this.rows=rows this.columns=columns this.border=0 this.data=new Array(rows*columns) this.header=new Array(rows*columns) this.getValue=table_getValue this.setValue=table_setValue this.set=table_set this.isHeader=table_isHeader this.makeHeader=table_makeHeader this.makeNormal=table_makeNormal this.makeHeaderRow=table_makeHeaderRow this.makeHeaderColumn=table_makeHeaderColumn this.write=table_write } table 객체 사용하기 <HTML> <HEAD> <TITLE>Defining Object Types</TITLE <SCRIPT LANGUAGE="JavaScript" SRC="table.js"><!- //-></SCRIPT> </HEAD> <BODY> <H1>Defining Object Types</H1> <SCRIPT LANGUAGE="JavaScript"><!- t=new table(3,4) contents=new Array("This","is","a","test","of","the","table","object.","Let's","see","it","work") t.set(contents) t.border=4 t.makeHeaderColumn(0) t.write(document) //-></SCRIPT> </BODY> </HTML> 객체 유형에 프로퍼티와 메소드 추가 : prototype 프로퍼티를 통해서 인스턴스화할 수 있는 미리 정의된 객체 유형에 프로퍼티와 메소드 추가 사용 예 <HTML> <HEAD> <TITLE>Updating Object Types </TITLE> <SCRIPT LANGUAGE="JavaScript" SRC="table.js"><!- //-></SCRIPT> </HEAD> <BODY> <H1>Updating Object Types</H1> <SCRIPT LANGUAGE="JavaScript"><!- function table_colorWrite(doc){ ........ 함수 정의 ........ ........ } t=new table(3,4) table.prototype.bgColor="Cyan" table.prototype.colorWrite=table_colorWrite ............. ............. t.colorWrite(document) //-></SCRIPT> </BODY> </HTML> 프로퍼티와 메소드 삭제 delete objectName.propertyName delete objectName.methodName 브라우저 객체 객 체 용 도 window 객체 브라우저 윈도우나 윈도우 안에 있는 프레임에 액세스할 때 사용한다. 프로퍼티와 메소드를 참조할 때, window 객체가 존재하는 경우에는 "window."접두사를 붙일 필요가 없다 document 객체 현재 윈도우에 로드되어 있는 문서에 액세스할 때 사용한다. document 객체는 컨텐트를 제공하는 HTML 문서, 즉 HEAD와 BODY 태그가 있는 문서를 의미한다. location 객체 URL을 나타낼 때 사용한다. 이 객체는 URL객체를 만들거나 URL의 일부분에 액세스하거나 기존의 URL을 수정할 때 사용할 수 있다. history 객체 한 윈도우 안에서 액세스된 URL의 히스토리를 유지할 때 사용 frame 객체, frames 배열 HTML 프레임에 액세스할 때 사용 frames 배열은 윈도우안에 있는 모든 프레임에 액세스할 때 사용 link 객체, links 배열 하이퍼텍스트 링크의 텍스트 기반이나 이미지 기반 소스 앵커(anchor)에 액세스할 때 사용 links배열은 문서 안에 있는 모든 link 객체에 액세스할 때 사용한다. I.E.는 link 객체의 anchor객체를 결합한다. anchor 객체, anchors 배열 하이퍼텍스트 링크의 타켓에 액세스할 때 사용 anchors 배열은 문서 안에 있는 모든 anchor 객체에 액세스할 때 사용 image객체, images 배열 HTML 문서에 삽입되어 있는 이미지에 액세스할 때 사용 images 배열은 문서 안에 있는 모든 image 객체에 액세스할 때 사용 area 객체 클라이언트측 이미지맵 안에 있는 영역에 액세스할 때 사용 applet 객체, applets 배열 Java 애플릿에 액세스할 때 사용 애플릿 배열은 문서 안에 있는 모든 애플릿에 액세스할 때 사용 event 객체, Event 객체 이벤트 발생에 대한 정보에 액세스할 때 사용 event 객체는 특정 이벤트에 대한 정보 제공 Event 객체는 이벤트를 식별하는 데 사용하는 상수 제공 form 객체, forms 배열 HTML 폼에 액세스시 사용 forms 배열은 문서 안에 있는 모든 폼에 액세스시 사용 element 객체 폼 안에 들어있는 모든 폼 요소에 액세스시 사용 text 객체 폼의 텍스트 필드에 액세스시 사용 textarea 객체 폼의 텍스트 영역 필드에 액세스시 사용 radio 객체 폼의 라디오 버튼 세트에 액세스하거나 세트 안에 있는 각각의 버튼에 액세스할 때 사용 checkbox 객체 폼의 체크 박스에 액세스할 때 사용 button 객체 Submit나 Reset 버튼이 아닌 폼 버튼에 액세스시 사용 reset 객체 폼의 Reset 버튼에 액세스시 사용 selet 객체 폼의 선택 리스트에 액세스시 사용 option 객체 option 객체는 선택 리스트의 요소에 액세스시 사용 password 객체 폼의 패스워드 필드에 액세스시 사용 hidden 객체 폼의 숨겨진 필드에 액세스시 사용 FileUpload 객체 폼의 파일 업로드 요소에 액세스시 사용 navigator 객체 스크립트를 실행하고 있는 브라우저에 대한 정보에 액세스시 사용 screen 객체 사용자의 화면의 색상 깊이와 크기에 대한 정보에 액세스시 사용 embed 객체, embeds 배열 삽입된 객체에 액세스시 사용 embeds 배열은 문서 안에 삽입된 모든 객체에 대한 액세스 제공 mimeType 객체, mimeTypes 배열 브라우저가 제공하는 특징 MIME 유형에 대한 정보에 액세스시 사용 mimeTypes 배열은 제공하는 모든 mimeType 객체의 배열 I.E.는 빈 배열을 리턴하면서 mimeTypes에 대해서 임시적으로 지원 plugin 객체, plugins 배열 특정 브라우저 플러그인에 대한 정보에 액세스시 사용 plugins 배열은 브라우저가 지원하는 모든 플러그인의 배열 I.E.는 빈 배열을 리턴하면서 plugins에 대해서 임시적으로 지원 window 객체 모든 브라우저 스크립트의 기본적인 것으로, 브라우저가 자동으로 정의하는 최상위 레벨의 객체이다. 현재 열려 있는 각 윈도우에 대해 별도의 window 객체가 정의된다. window 객체의 프로퍼티 프로퍼티 설 명 closed 윈도우가 닫혀 있는지 식별 defaultStatus 브라우저 윈도우의 하단의 상태바에 나타나는 디폴트 상대 메시지를 지정 document 윈도우에 표시되어 있는 현재 문서를 지정하는 객체 frames 윈도우 객체에 들어 있는 모든 프레임 객체로 구성된 배열 history 마지막으로 윈도우로 로드된 URL의 리스트를 포함하는 윈도우의 히스토리 객체 length window에 들어 있는 프레임의 수 식별 location window 객체와 관련된 URL을 지정하는 객체 name 윈도우의 이름 지정 offscreenBuffering 윈도우 정보의 오프스크린 버퍼링이 사용될 것인지를 지정하는 부울값 오프스크린 버퍼링은 윈도우를 나타내기 전에 윈도우의 모든 요소를 로드할 때 사용 opener 윈도우를 만들거나 열 수 있도록 해주는 window 객체 지정 parent 특정 윈도우를 포함하는 윈도우를 지정하는 시너님 self 참조될 현재 윈도우를 지정하는 시너님 status 브라우저 윈도의 하단의 상태 표시줄에 나타날 임시 메시지를 지정 top 중첩된 일련의 윈도우에서 맨 위에 있는 브라우저 윈도우를 의미하는 시너님 window 참조될 현재 윈도우를 식별하는 시너님 window 객체의 메소드 메 소 드 설 명 alert(text) 경고 다이얼로그 박스를 표시 blur() 포커스를 윈도우에서 옮긴다 setInterval(expression,milliseconds) 지정된 타임아웃 인터벌이 지난 이후에 표현식을 반복해서 평가하거나 함수를 불러온다. clearInterval(interval) 이전에 설정된 인터벌 타이머를 클리어 setTimeout(expression,milliseconds) 타임아웃 기간이 지난 이후에 표현식을 평가하거나 함수를 호출한다. clearTimeout(timer) 이전에 설정된 타임아웃을 클리어 close() 지정된 윈도우를 닫는다. confirm(text) 확인 다이얼로그 박스를 나타낸다. focus() 윈도우로 포커스를 가져간다. open(url,name,[options]) 새로운 윈도우를 열고 새로운 window 객체를 만듬 prompt(text,defaultInput) 프롬프트 다이얼로그 박스를 나타낸다. scroll(x,y) 윈도우를 특정 위치까지 스크롤한다. open() 메소드의 옵션 옵 션 값 설 명 toolbar yes no 윈도우에 툴바 제공 location yes no 윈도우에 위치 필드 제공 directories yes no 디렉토리 버튼 제공 status yes no 상태 표시줄 제공 menubar yes no 메뉴바 제공 scrollbars yes no 스크롤 바 제공 resizable yes no 윈도우 크기 조절 가능 width 정수 윈도우의 폭(픽셀) height 정수 윈도우의 높이(픽셀) frame 객체 프레임은 윈도우를 독립된 표시 영역들로 분할한 후, 이 영역들에 표시되는 정보들을 강력하게 컨트롤 할 수 있게 해준다. 프레임 객체의 프로퍼티와 메소드는 window 객체와 같지만, close() 메소드는 지원하지 않는다. document 객체 JavaScript 에서 아주 중요한 객체로, 이 객체를 사용하면 로드될 문서를 업데이트하고 로드된 문서 안에 있는 HTML 요소에 액세스할 수 있다. 프로퍼티 설 명 alinkColor <BODY> 태그의 alink 속성의 값 지정 anchor 문서에 들어 있는 배열을 참조하는 객체 anchors 문서에 들어 있는 모든 앵커의 배열 applet 문서에 들어 있는 애플릿을 참조하는 객체 applets 문서에 들어 있는 모든 애플릿의 배열 area 문서 안의 이미지맵 영역을 참조하는 객체 bgColor <BODY> 태그의 bgColor 속성의 값 식별 cookie 쿠키의 값 식별 domain 문서가 로드되는 서버의 도메인 이름 식별 embeds 문서안의 모든 플러그인의 배열 fgColor <BODY> 태그의 text 속성값 지정 form 문서 안의 폼을 참조하는 객체 Forms[] 문서 안의 모든 폼의 배열 image 문서 안의 이미지를 참조하는 객체 Images[] 문서 안의 모든 이미지의 배열 lastModified 문서가 마지막으로 수정된 날짜 link 문서 안의 링크를 참조하는 객체 links 문서 안의 모든 링크의 배열 linkColor <BODY> 태그의 link 속성의 값 식별 plugin 문서 안의 플러그인을 참조하는 객체 plugins[] 브라우저가 지원하는 플러그인을 나타내는 객체의 배열 referrer 문서에 대한 링크를 제공하는 문서의 URL title 문서의 타이틀 URL 문서의 URL vlinkColor <BODY> 태그의 vlink 속성의 값 식별 메 소 드 설 명 close() 문서의 객체를 만드는 데 사용된 스트림 open([mimeType][,"replace"]) 선택적인 MIME 유형으로 문서 객체를 만들 때 사용되는 흐름을 개시한다. "replace" 파라미터는 text/html MIME 유형과 함께 사용되어 히스토리 리스트에 있는 현재 문서를 대체 write(expr1[,expr2...,exprN]) 문서에 표현식의 값을 기록 write(expr1[,expr2...,exprN]) 개행 문자가 다음에 따라오는 문서에 표현식의 값 기록 navigator 객체 navigator 객체는 window 객체와 마찬가지로 브라우저 객체 모델에서 최상위 레벨의 객체이며, 스크립트를 실행할 때 사용되는 브라우저의 종류와 버전에 대한 정보 제공한다. 프로퍼티 브라우저 지원 설 명 appCodeName N2, I.E3 브라우저 색상 이름 AppMinorVersion I.E4 브라우저 버전 번호 appName N2, I.E3 브라우저 이름 appVersion N2, I.E3 브라우저의 버전 browserLanguage I.E4 브라우저에 설정되어 있는 언어 connectionSpeed I.E4 브라우저가 네트워크에 연결되는 속도 cookieEnabled I.E4 브라우저가 쿠키를 허용하도록 설정되어 있는지의 여부 cpuClass I.E4 브라우저 실행시 사용되는 마이크로프로세서의 유형 onLine I.E4 브라우저가 현재 온라인 연결을 가지고 있는지 여부 Language N4, I.E4 브라우저에 설정되어 있는 언어 mimeTypes N3, I.E4 현재 브라우저가 지원하는 모든 MIME 유형의 배열 platform N4, I.E4 브라우저가 실행되는 운영체제 플랫폼 plugins N3, I.E4 현재 브라우저에 설치된 모든 플러그인의 배열 systemLanguage I.E4 운영체제의 디폴트 언어 userAgent N2, I.E3 브라우저에서 서버로 전송된 HTTP 프로토콜의 사용자 에이전트 헤더 userLanguage I.E4 사용자가 사용하는 언어 userProfile I.E4 사용자 프로파일 정보에 대한 액세스를 제공하는 객체 navigator 객체의 메소드 메 소 드 설 명 javaEnabled() 사용자가 브라우저의 Java 기능을 켜두었는지의 여부를 나타내는 부울값 리턴 taintEnabled() 사용자가 data tainting(보안 메커니즘)을 활성화했는지 여부를 나타내는 부울값 리턴 preference 서명이 되지 않은 스크립트가 보안 관련 프로퍼티를 얻고 설정할 때 사용 event 객체 event 객체의 프로퍼티 프로퍼티 브라우저 설 명 data N DragDrop 이벤트로 인해 드롭된 객체의 URL이 들어있는 스트링 배열 height, width N 윈도우나 프레인의 길이와 너비(픽셀표시) pageX, pageY N 픽셀로 나타낸 커서의 수평/수직 위치(페이지에서의 상대적위치) screenX, screenY N, I.E 픽셀로 나타낸 커서의 수평/수직 위치(화면에서의 상대적 위치) layerX, layerY N 픽셀로 나타낸 커서의 수평/수직 위치, 이벤트가 발생한 레이어에 대한 상대적 위치. Resize 이벤트와 함께 사용하면 layerX와 layerY가 이벤트가 타겟으로 하는 객체의 길이와 너비 지정 clientX와 clientY I.E 픽셀로 나타낸 커서의 수평/수직 위치, 이벤트가 발생한 웹페이지에서의 상대적 위치 offsetX, offsetY I.E 픽셀로 나타낸 커서의 수평/수직 위치, 이벤트가 발생한 컨테이너에 대한 상대적 위치 X, Y I.E 픽셀로 나타낸 커서의 수평/수직 위치, 이벤트가 발생한 문서에 대한 상대적 위치 target N 이벤트가 전송된 원래 객체 srcElement I.E 이벤트가 전송된 원래 객체 type N, I.E 발생한 이벤트 유형 which N 눌려진 마우스 버튼(왼:1, 가:2, 오:3)이나 눌려진 키의 ASCII값 keyCode I.E 키 누름과 연관된 Unicode 키 코드를 식별 button I.E 이벤트가 발생했을 때 눌려진 마우스 버튼 식별(0:눌려진버튼없음, 1:왼, 2:오, 4:가) modifiers N 마우스나 키 이벤트와 연관된 수정자 키(ALT_MASK,CONTROL_MASK,SHIFT_MASK,META_MASK)를 식별 altkey,ctrlkey,shiftkey I.E true나 false로 설정하여 이벤트가 발생했을 때 Alt키와 Control키, Shift 키 중에 어떤 것이 눌려졌는지 알려준다. cancelBubble I.E true나 false로 설정하여 이벤트 버블링을 취소하거나 활성화한다. fromElement, toElement I.E 이동 중인 HTML 요소 지정 reason I.E 데이터 소스 객체에 대한 데이터 전송 상태를 나타내는데 사용 returnValue I.E true나 false로 설정하여 이벤트 핸들러의 리턴값을 나타낸다. 이벤트 핸들러에서 true나 false를 리턴하는 것과 같다. srcFilter I.E onfilterchange 이벤트를 시작하는 filter객체 지정 screen 객체 screen 객체의 프로퍼티 height : 사용자의 화면의 현재 높이(픽셀) width : 사용자의 화면의 현재 너비(픽셀) colorDepth : 사용자의 화면/비디오 카드에서 현재 지원하는 색상당 바이트 수 form 객체 document 객체의 프로퍼티로 액세스되고, form 객체는 문서 안의 폼에 액세스할 수 있도록 해주고, 폼 관련 이벤트에 응답을 할 수 있도록 해주기 때문에 중요하다. form 객체의 프로퍼티 프로퍼티 설 명 action <FORM> 태그의 HTML action 속성에 대한 액세스 제공 button GUI 컨트롤 버튼을 나타내는 객체 checkbox 체크 박스 필드를 나타내는 객체 elements 폼 안에 포함되어 있는 모든 필드와 GUI 컨트롤을 포함하는 배열 encoding <FORM> 태그의 HTML enctype 속성에 대한 액세스 제공 FileUpload 파일 업로드 폼 필드를 나타내는 객체 hidden 숨겨진 폼 필드를 나타내는 객체 length elements 배열의 길이에 대한 액세스 제공 method <FORM> 태그의 HTML method 속성에 대한 액세스 제공 name 폼의 이름 식별 password 패스워드 필드를 나타내는 객체 radio 라디오 버튼 필드를 나타내는 객체 reset reset 버튼을 나타내는 객체 select 선택 항목 리스트를 나타내는 객체 submit submit 버튼을 나타내는 객체 target <FORM> 태그의 HTML target 속성에 대한 액세스 제공 text 텍스트 필드를 나타내는 객체 textarea 텍스트 영역 필드를 나타내는 객체 form 객체의 메소드 메 소 드 설 명 handleEvent() 지정된 이벤트에 대한 폼의 이벤트 핸들러를 호출할 때 사용 submit() 폼을 제출시 사용 reset 폼의 엔트리를 디폴트 값으로 재설정시 사용 form 요소 객체의 프로퍼티 객 체 프로퍼티 설 명 button name 버튼의 name 속성에 대한 액세스 제공 type 객체의 유형 지정 value 객체의 값 지정 checkbox checked 체크박스가 현재 체크되어 있는지를 식별 defaultChecked 체크박스가 디폴트로 체크되어 있는지 식별 name 체크박스의 HTML name 속성에 대한 액세스 제공 type 객체의 유형 식별 value 객체의 값 식별 FileUpload name 객체의 name 속성에 대한 액세스 제공 type 객체의 type 속성에 대한 액세스 제공 value 객체의 값 식별 hidden name 객체의 name 속성에 대한 액세스 제공 type 객체의 유형 식별 value 객체의 값 식별 password defaultChecked 객체의 디폴트 값 식별 name 객체의 name 속성에 대한 액세스 제공 type 객체의 유형 식별 value 객체의 값 식별 radio checked 라디오 버튼이 체크되어 있는지 식별 defaultChecked 라디오 버튼이 디폴트로 체크되어 있는지 식별 name 객체의 name 속성에 대한 액세스 제공 type 객체의 유형 식별 value 객체의 값 식별 reset name 객체의 name 속성에 대한 액세스 제공 type 객체의 유형 식별 value 객체의 값 식별 select length 선택 리스트의 길이 식별 name 객체의 name 속성에 대한 액세스 제공 option 선택 리스트가 제공하는 옵션 식별하는 배열 selectedIndex 선택 리스트 안에서 처음 선택된 옵션 식별 type 객체의 유형 식별 submit name 객체의 name 속서에 대한 액세스 제공 type 객체의 유형 식별 value 객체의 값 식별 text defaultValue 텍스트 필드에 나타나는 디폴트 텍스트를 식별 name 객체의 name 속성에 대한 액세스 제공 type 객체의 유형 식별 value 객체의 값 식별 textarea defaultValue 텍스트 영역 필드에 나타나게 되는 디폴트 텍스트 식별 name 객체의 name 속성에 대한 액세스 제공 type 객체의 유형 식별 value 객체의 값 식별 form 요소 객체의 메소드 객 체 메 소 드 설 명 button Click() 클릭된 버튼 시뮬레이트 blur() 포커스 잃음 focus() 포커스 맞춤 checkbox Click() 클릭된 체크 박스 시뮬레이트 blur() 포커스 잃음 focus() 포커스 맞춤 FileUpload blur() 포커스 잃음 focus() 포커스 맞춤 select() 입력 영역을 선택 hidden 없음 password blur() 포커스 잃음 focus() 포커스 맞춤 select() 패스워드 필드에 나타나는 텍스트를 하이라이트 radio Click() 라디오 버튼 클릭 시뮬레이트 blur() 포커스 잃음 focus() 포커스 맞춤 reset Click() 리셋 버튼 클릭 시뮬레이트 blur() 포커스 잃음 focus() 포커스 맞춤 select blur() 포커스 잃음 focus() 포커스 맞춤 submit Click() 제출 버튼 클릭 시뮬레이트 blur() 포커스 잃음 focus() 포커스 맞춤 text blur() 포커스 잃음 focus() 포커스 맞춤 select() 텍스트 필드에 있는 텍스트 하이라이트 textarea blur() 포커스 잃음 focus() 포커스 맞춤 select() 텍스트 영역에 있는 텍스트 하이라이트 location 객체 윈도우에 로드되어 있는 현재 문서의 URL에 액세스하거나 새로운 문서를 로드할 때 사용 location 객체의 프로퍼티 프로퍼티 설 명 hash URL의 앵커 부분(존재하는 경우) host URL의 hostname:port 부분 hostname URL의 host부분 href 전체 URL pathname URL의 경로명 부분 port URL의 포트 부분 protocol URL의 프로토콜 부분 search URL의 쿼리 스트링 부분 location 객체의 메소드 reload() : 윈도우의 현재 문서를 브라우저의 Reload 버튼에서 사용하는 정책에 따라 다시 로드 Every time : 문서는 매번 서버에서 다시 로드 Once per session : 서버의 문서의 날짜가 캐시에 저장되어 있는 문서보다 더 최신 날짜라는 것을 나타내면 문서는 세션당 한 번씩 서버에서 다시 로드된다. 문서가 캐시에 없는 경우에는 서버에서 로드 Never : 문서는 가능하면 캐시에서 다시 로드, 그렇지 않으면 서버에서 로드 replace() : URL을 파라미터로 취하여, 현재 문서 히스토리 목록에 있는 현재 문서위로 그 URL의 문서를 로드, 그러면 브라우저의 Back버튼을 클릭하여 이전 문서로 돌아갈 수 없음 link 객체 link 객체는 document 객체의 프로퍼티로, 문서에 들어있는 텍스트나 이미지 링크를 캡슐화 links 배열은 문서에 들어있는 모든 링크의 배열 link 객체의 메소드 handleEvent() : event 객체를 인자로 취하며 그 이벤트에 대해 적당한 이벤트 핸들러 호출 link 객체의 프로퍼티 프로퍼티 설 명 hash URL의 앵커 부분(존재하는 경우) host URL의 hostname:port 부분 hostname URL의 host부분 href 전체 URL pathname URL의 경로명 부분 port URL의 포트 부분 protocol URL의 프로토콜 부분 search URL의 쿼리 스트링 부분 target 링크의 HTML, target 속성 anchor 객체 HTML 문서 안에서 이름이 지정된 오프셋으로 사용되는 앵커 의미 anchors 배열에는 문서의 모든 앵커가 들어있음 프로퍼티나 메소드 또는 이벤트를 전혀 가지고 있지 않음 HTML 문서와 관련하여 정의된 이름이 지정된 오프셋을 추적할 때 사용 anchor 객체는 HREF 속성을 포함하는 경우에 link 객체가 된다. history 객체 history 객체의 프로퍼티 current : 윈도우에 나타나는 현재 문서의 URL length : History 리스트의 길이 next : History 리스트에서의 다음 URL previous : History 리스트에서의 이전 URL history 객체의 메소드 back() : History 리스트에 이전 문서를 로드. 브라우저의 Back 버튼을 클릭하는 것과 같은 효과 forward() : History 리스트에 다음 문서를 로드. 브라우저의 Forward 버튼을 클릭하는 것과 같은 효과 go() : History 리스트에 있는 특정 문서로 감 go(n) : n>0인 경우, 이 메소드는 History 리스트에서 n개의 엔트리가 앞에 있는 문서를 로드, n=0인 경우에는 현재 문서가 다시 로드되고, n<0인 경우엔 History 리스트에서 n개의 엔트리 뒤에 있는 문서를 로드 go(string) : go()는 이 스트링을 서브스트링으로 갖고 있는 URL의 History 리스트에서 가장 가까운 문서를 로드 image 객체 document 객체의 프로퍼티 문서와 함께 로드된 이미지에 대한 액세스 제공 images 배열은 문서 안에 지정되어 있는 각각의 <IMG>태그에 대한 엔트리가 들어있음 image 객체 유형을 사용하면 키워드와 생성자로 새로운 image 객체를 명시적으로 만들 수 있다. Image() 생성자는 웹페이지의 일부로서 처음에 나타나지 않는 이미지를 만들고 미리 로드할 때 사용한다. 이러한 image 객체는 브라우저의 캐시에 저장되면 이미 나타난 이미지를 대체할 때 사용 * Image() 생성자를 사용하여 캐시된 이미지 만드는 예 cachedImage=new Image() cachedImage.src="myImage.gif" = > 첫번째 문장은 새로운 image 객체를 만들고 그것을 cachedImage 변수에 대입하고, 두번째 문장은 image 객체의 src프로퍼티를 myImage.gif 이미지 파일로 설정한다. 이 경우 myImage.gif는 브라우저 캐시로 로드된다. 그러면 로드된 이미지는 cachedImage 변수를 사용하여 참조 가능하다. image 객체의 프로퍼티 프로퍼티 설 명 border <:IMG> 태그의 BORDER 속성의 값 complete 이미지가 완전히 로드되었는지 식별 height <:IMG> 태그의 HEIGHT 속성의 값 hspace <IMG> 태그의 HSPACE 속성의 값 lowsrc <IMG> 태그의 LOWSRC 속성의 값 name <IMG> 태그의 NAME 속성의 값 prototype image 객체에 사용자 지정 프로퍼티를 추가할 때 사용 src <IMG> 태그의 SRC 속성의 값 vspace <IMG> 태그의 VSPACE 속성의 값 width <IMG> 태그의 WIDTH 속성의 값 area 객체 이미지맵은 여러 가지 다른 영역으로 나누어져 있는 이미지로서 각각의 영역은 자체 URL과 관련되어 있다. 그리고 이러한 영역과 관련된 사용자 처리 방식으로 area 객체를 제공 area 객체의 프로퍼티 프로퍼티 설 명 hash area 객체의 HREF 속성의 파일 오프셋 부분 host area 객체의 HREF 속성의 호스트 이름 부분 hostname area 객체의 HREF 속성의 host:port 부분 href area 객체의 완전한 HREF 속성 pathname area 객체의 HREF 속성의 경로명 부분 port area 객체의 HREF 속성의 포트 부분 protocol area 객체의 HREF 속성의 프로토콜 부분 search area 객체의 HREF 속성의 쿼리 스트링 부분 target area 객체의 TARGET 속성 Array 객체 Array 객체를 사용하면 배열을 객체처럼 액세스 가능 Array 객체의 프로퍼티 length : 배열의 길이 식별 prototype : 모든 객체 유형이 지원하는 프로퍼티로 객체 유형에 대해 추가적인 프로퍼티 및 메소드 정의 가능 Array 객체의 메소드 toString() : 배열의 스트링 버전 리턴, 배열 요소는 컴마로 구분 join(separator) : 배열의 스트링 버전 리턴, 배열 요소는 seperator 스트링으로 구분, 분리자가 없으면 컴마로 구분 reverse() : 배열의 요소를 역순으로 바꿈 sort(comparisionFunction) : 비교 연산에 따라 배열의 요소 정렬. 비교 함수가 지정되면, 배열 요소는 사전순서로 정렬. 비교 연산이 지정되면 두개의 파라미터 p1,p2를 취하고, p1이 p2보다 작은 경우에는 음의 정수를 리턴하고, 같으면 0을 리턴하고, p1이 p2보다 크면 양의 정수 리턴 Boolean 객체 Boolean 객체를 사용하면 부울값은 객체로서 액세스 가능 Boolean 객체는 생성자에 대한 인자로서 값을 식별하여 만들어짐 myBoolean=new Boolean(false) yourBoolean=new Boolean(true) Boolean 객체의 프로퍼티 prototype : 모든 객체 유형이 지원하는 프로퍼티로 객체 유형에 대해 추가적인 프로퍼티 및 메소드 정의 가능 Boolean 객체의 메소드 toString() : 부울값에 해당하는 스트링 리턴 valueOf() : 객체의 값에 따라 true나 false로 리턴 Date 객체의 메소드 메 소 드 설 명 getDate() getUTCDate() setDate() setUTCDate() Date 객체의 날짜를 설정하거나 리턴 getDay() getUTCDay() Date 객체의 한 주의 날짜를 설정하거나 리턴 getHours() getUTCHours() setHours() setUTCHours() Date 객체의 시간를 설정하거나 리턴 getMilliseconds() getUTCMilliseconds() setMilliseconds() setUTCMilliseconds() Date 객체의 밀리초 값을 설정하거나 리턴 getMinutes() getUTCMinutes() setMinutes() setUTCMinutes() Date 객체의 분을 설정하거나 리턴 getMonth() getUTCMonth() setMonth() setUTCMonth() Date 객체의 달을 설정하거나 리턴 getSeconds() getUTCSeconds() setSeconds() setUTCSeconds() Date 객체의 초를 설정하거나 리턴 getTime() getUTCTime() Date 객체의 시간을 설정하거나 리턴 getTimeZoneOffset() Date 객체의 시간대 오프셋(분)을 리턴 getYear() getFullYear() getUTCFullYear() setYear() setFullYear() setUTCFullYear() Date 객체의 연도를 리턴하거나 설정한다. 완전한 연도를 나타내는 방법으로 4자리 연도 값을 사용한다. toGMTString() 날짜를 Internet GMT(Greenwich Mean Time) 포맷의 스트링으로 변환 toLocaleString() 날짜를 로케일(locale)포맷의 스트링으로 변환. 로케일 포맷이란 사용자가 위치해 있는 지형적 위치에서 일반적으로 사용하는 포맷 의미 toString() Date 객체의 스트링 값을 리턴 valueOf() 1970년 1월 1일 자정 이후의 밀리초 값 리턴 toUTCString() UTC에서 시간을 나타내는 스트링 값을 리턴 Date 생성자 생 성 자 설 명 Date() 현재 날짜와 시간으로 Date 인스턴스 만듬 Date(dateString) dateString 파라미터에 지정되어 있는 날짜로, Date 인스턴스를 만든다. dateString의 포맷은 "월,일,연도,시:분:초" Date(milliseconds) 1970년 1월 1일 자정 이후 지정된 밀리초 값으로 Date 인스턴스를 만든다. Date(year,month, day,hours,minutes, seconds,milliseconds) 년,월,일,시,분,초,밀리초 정수에 따라 지정된 날짜로 Date 인스턴스를 만든다. 연도와 월 파라미터는 제공되어야 하고 다른 나머지 파라미터가 포함되면 앞에 오는 모든 파라미터가 제공되어야 Function 객체 함수를 객체처럼 액세스 가능하고, 이 객체는 스크립트를 실행하는 동안에 함수를 동적으로 만들고 호출할 때 사용 Function 객체는 함수의 파라미터와 본문을 Function() 생성자에 제공하면 된다. variable=new Function("p1","p2", ... ,"pn","body") Function 객체 프로퍼티 length : 함수에 대해 정의된 파라미터의 숫자 식별 prototype : 모든 객체 유형이 지원하는 프로퍼티로 객체 유형에 대해 추가적인 프로퍼티 및 메소드 정의 가능 arguments : 호출시 함수에 전달되는 인자를 가리키는 배열 caller : 특정 함수를 호출한 함수를 가리킴 Function 객체의 메소드 toString() : 함수의 스트링 형태 리턴 valueOf() : 함수 자체 리턴 Global 객체 new Global() 을 통해서 명시적으로 만들거나, 참조할 수 없다. 대신 해당 프로퍼티와 메소드가 전역 변수와 함수로 직접 참조됨 Global 객체의 프로퍼티 Nan : 숫자가 아니라는 의미 Infinity : 양수 무한대 값 의미 Global 객체의 메소드 escape(string) : string을 새로운 스트링으로 변환 eval(x) : 표현식 x의 값을 계산하고 리턴 inFinite(number) : number가 유한하면 true를 리턴하고, 무한하면 false를 리턴 inNaN(number) : number가 숫자가 아니면 true를 리턴하고, 숫자이면 false를 리턴 parseFloat(string) : string을 부동 소수 값으로 파싱 parseInt(string,radix) : string을 밑이 radix인 정수로 파싱 unescape(string) : escape()에 들어 있는 스트링을 원래의 값으로 되돌린다. Math 객체 수학적 상수와 함수의 표준 라이브러리 제공 Math의 특수 인스턴스는 Math가 내장 객체이고 객체 유형이 아니기 때문에 만들어지지 않는다. Math 프로퍼티 프로퍼티 설 명 E Euler의 상수. 이것은 계산을 하는 어디에서나 발견할 수 있으며 자연대수의 기초 LN2 2의 자연대수. 이것은 자연대수와 밑이 2인 대수 사이의 전환에 사용되는 간단한 상수 LN10 10의 자연대수. LN2와 마찬가지로 대수 변환에 사용 LOG2E 밑이 2인 E의 대수. 이것은 밑이 10인 대수를 밑이 E인 대수로 변환시 사용 PI 원의 원주 대 지름의 비율 SQRT1_2 1/2의 제곱근은 많은 삼각법 계산에서 사용 SQRT2 2의 제곱근은 대수식에서 흔히 사용 Math 메소드 메 소 드 설 명 abs(x) x의 절대값 리턴 acos(x) x의 아크 코사인값 라디안으로 리턴 asin(x) x의 아크 사인값 라디안으로 리턴 atan(x) x의 아크 탄젠트 값을 라디안으로 리턴 atan2(x,y) (x,y)에 해당하는 극좌표의 각도를 리턴 ceil(x) x보다 크거나 작은 최소 정수값 리턴 cos(x) x의 코사인 값 리턴 exp(x) eX를 리턴 floor(x) x보다 작거나 같은 최대 정수값 리턴 log(x) x의 자연대수 리턴 max(x,y) x, y 중 큰 값 리턴 min(x,y) x, y 중 작은 값 리턴 pow(x,y) xy를 리턴 random() 0과 1사이의 임의의 숫자 리턴 round(x) x의 가장 가까운 정수로 반올림되는 값 리턴 sin(x) x의 사인값 리턴 sqrt(x) x의 제곱근 리턴 tan(x) x의 탄젠트 값 리턴 Number 객체 Number 객체 유형을 사용하면 숫자를 객체로 다룰 수 있다. Number 객체의 프로퍼티 MAX_VALUE : 숫자는 가능한 최대 수치값 MIN_VALUE : 숫자는 가능한 최소 수치값 NaN : 숫자가 아니다 NEGATIVE_INFINITY : 숫자가 음수 무한대 값 POSITIVE_INFINITY : 숫자가 양수 무한대 값 prototype : 모든 객체 유형이 지원 Number 객체의 메소드 toString(radix) : 밑이 radix인 숫자를 나타내는 스트링 리턴 valueOf() : Number 객체의 수치값 리턴 Object 객체 Object 객체는 다른 모든 객체들이 파생되어 나가는 기반 객체로 이것의 프로퍼티와 메소드는 다른 객체 유형들에서 사용 가능 Object 객체의 프로퍼티 prototype : 모든 객체 유형이 지원 constructor : 객체 생성자의 이름 식별 Object 객체의 메소드 toString() : 객체를 스트링 표현으로 바꾸는 역할 valueOf() : 객체와 관련된 경우의 원시값(숫자,스트링,부울값)을 리턴하고, 그렇지 않은 경우에는 객체 자체를 리턴 String 객체 스트링을 객체로 액세스 가능 String 객체의 프로퍼티 length : 문자에서 스트링의 길이 알아내는 역할 prototype : 모든 객체 유형이 지원 String 메소드 메 소 드 설 명 charAt(index) 메소드가 적용되는 스트링의 지정된 인덱스에 있는 문자로 구성된 스트링을 리턴 charCodeAt(index) 지정된 인덱스의 문자의 Unicode 인코딩 리턴 fromCharCode(codes) 문자 코드의 컴마로 구분된 시퀀스에서 스트링 만듬 indexOf(pattern) 스트링안에 들어있는 pattern 파라미터가 지정한 첫 번째 스트링의 인덱스 리턴, 패턴이 스트링 안에 들어있지 않으면 -1 리턴 indexOf(pattern,startIndex) startIndex가 지정한 위치에서 검색을 시작하는 것을 제외하면 indexOf(pattern) 메소드와 같다. lastIndexOf(pattern) 스트링에 들어 있는 pattern 파라미터가 지정한 마지막 스트링의 인덱스 리턴, 패턴이 스트링 안에 들어있지 않으면 -1 리턴 lastIndexOf(pattern,startIndex) startIndex가 지정한 위치에서 검색을 시작하는 것을 제외하면 lastIndexOf(pattern)과 같다. splitSeparator() 하나의 스트링을 분리자를 기반으로 하여 서브스트링의 배열로 분리 substring(startIndex) startIndex에서 시작하는 스트링의 서브스트링을 리턴 substring(startIndex,endIndex) startIndex에서 시작하고, endIndex에서 끝나는 스트링의 서브스트링을 리턴 toLowerCase() 소문자로 변환된 스트링의 복사본 리턴 toString() 객체의 스트링 값을 리턴 toUpperCase() 대문자로 변환된 스트링의 복사본 리턴 [출처] 자바스크립트의 모든것 |작성자 JYP
이온디
이온디 10년 전
http://blog.naver.com/choiye84/130130406299 jquery validation check plugin 을 발견햇다!. 쓰기 편한것 같고 깔끔한것 같다. validation말고도 Accordion, Autocomplete, Message, Password Validation 등등이 있다. 쓰기 편하게 되어져 있는것 같고, 한가지 아쉬운 점은 아이디 같은 경우는 ajax로 호출해서 사용유무를 가져왔으면 하는데.. … http://blog.naver.com/choiye84/130130406299 jquery validation check plugin 을 발견햇다!. 쓰기 편한것 같고 깔끔한것 같다. validation말고도 Accordion, Autocomplete, Message, Password Validation 등등이 있다. 쓰기 편하게 되어져 있는것 같고, 한가지 아쉬운 점은 아이디 같은 경우는 ajax로 호출해서 사용유무를 가져왔으면 하는데.. 그 기능이 없는것 같다? 웅? -,.- 사이트 url은 http://bassistance.de/jquery-plugins/ 이고, 여기 validation에 대해서 자세히? 소개한 어느 블러거님의 url도 같이 ! 투척! http://mytory.co.kr/archives/195 웅웅? jquery 진짜 짱인것 같다. 후후 다른 validation 좀 찾아봐야지~ ==================================추가================================= validation API http://docs.jquery.com/Plugins/Validation 여기서 보면 remote option도 있어서 ajax로 할 수도 있다! [출처] jquery Validation (http://bassistance.de)|작성자 빼빼로 이번에 찾은 좋은 jquery validation을 찾아서 공유 합니다.ㅎ 먼저 http://bassistance.de/jquery-plugins/jquery-plugin-validation/ 요기서 다운 받으시면 되구요. API 는 http://docs.jquery.com/Plugins/Validation 여기에 있습니다. 그럼 간단한 설명 하겠습니다. jquery.validate 를 다운 받습니다. 그리고 자바스크립트 선언을 해줍니다. <script type="text/javascript" src="/resource/js/validation/jquery.validate.min.js"></script> html 코딩 <tr> <th scope="row"><label for="id">아이디</label></th> <td> <div class="item"> <input type="text" name="id" title="" class="i_text" /> </div> </td> </tr> <tr> <th scope="row"><label for="nm">이름</label></th> <td> <div class="item"> <input type="text" id="nm" name="nm" title="" class="i_text" /> </div> </td> </tr> <tr> <th scope="row"><label for="pw">비밀번호</label></th> <td> <div class="item"> <input type="password" id="pw" name="pw" title="" class="i_text" maxlength="15" /> </div> </td> </tr> <tr> <th scope="row"><label for="pwchk">비밀번호 확인</label></th> <td> <div class="item"> <input type="password" id="pwchk" name="pwchk" title="" class="i_text" maxlength="15" /> </div> </td> </tr> 여기서 주의 할 점은 꼭 <lable> 태그를 추가 해줘야 한다는 것입니다. js코딩 $("#managerForm").validate({ rules:{ id:{ required:true ,remote:"/remote/getChkUserId.do" } ,nm:"required" ,pw:{ required:true ,minlength:6 ,maxlength:12 } ,pwchk:{ equalTo:"#pw" } ,email:{ required:true ,email:true } } ,messages:{ id:"입력해주세요." ,nm:"입력해주세요." ,pw:{ required:"입력해주세요." ,minlength:"6자 이상" ,maxlength:"12자 이하" } ,pwchk:{ equalTo:"일치하지 않습니다." } ,email:"입력해주세요." } }); validate에는 2개의 오브젝트가 있는데요. rules와 message 입니다. rules가 validate 체크 해주는 오브젝트고, message는 유효하지 않을 시 표현하는 메세지입니다. 그리고 remote 옵션을 이용하면 ajax로 아이디값 실시간으로 가져 올 수 도 있습니다. 더 자세한 사항은 위에 나와있는 api url에 가시면 더 자세하고 정확하게?! 확인 하 실 수 있습니다. css 코딩 input.error, textarea.error{ border:1px dashed red; } label.error{ margin-left:10px; color:red; } input.error, textarea.error{ border:1px dashed red; } label.error{ padding-left:5px; display:inline; color:red; } display:inline; 을 block; 으로 바꿔주면 input 태그 밑에 메세지가 표현됩니다^^ 이렇게 코딩 해주고 submit 해주면 아래와 같이 나옵니다. [출처] [jQuery] validation|작성자 빼빼로
이온디
이온디 10년 전
http://blog.javarouka.me/2012/01/javascripts-closure.html 자바스크립트를 조금 공부하다보면 바로 맞딱뜨리는 개념중 하나가 클로저 (Closure)라는 개념입니다. 클로저는 하늘에서 뚝 떨어진 용어도 아니고 프로그래밍 언어에 있는 용어중 하나로서, 언어마다 조금씩 다른 구현과 특성을 가지고 있는 재미있는 개념이죠. 글로 쓰게 되니 더욱 어렵습니다. 위 문장들은 결과적으로는 다 같은 뜻입니다. 그럼 예제 코드를 보… http://blog.javarouka.me/2012/01/javascripts-closure.html 자바스크립트를 조금 공부하다보면 바로 맞딱뜨리는 개념중 하나가 클로저 (Closure)라는 개념입니다. 클로저는 하늘에서 뚝 떨어진 용어도 아니고 프로그래밍 언어에 있는 용어중 하나로서, 언어마다 조금씩 다른 구현과 특성을 가지고 있는 재미있는 개념이죠. 글로 쓰게 되니 더욱 어렵습니다. 위 문장들은 결과적으로는 다 같은 뜻입니다. 그럼 예제 코드를 보죠. 클로저의 예를 들때 많이 예시가 되는 시퀀스 함수입니다. var sequencer = function() { var s = 0; return function() { return ++s; } }; /* seq 함수는 이런 구조를 가지고 반환될 것입니다 function seq() { return ++s; } */ var seq = sequencer(); seq(); // 1 seq(); // 2 seq(); // 3 보통의 언어라면 말도 안되는 코드입니다. 이미 sequencer 내부의 s 변수는 다른 언어로 치면 유효범위를 벗어난 쓸 수 없는 변수입니다. 자바같은 가비지 컬렉터를 가진 언어에서는 바로 가비지 컬렉팅의 대상이 되겠지요. 그러나 자바스크립트에서는 다릅니다. 내부 함수는 자신이 선언된 환경에 대해 연결을 갖습니다. 그래서 위 예제에서 반환된 함수는 자신이 선언될 때의 환경인 sequencer 의 유효범위에 대한 연결을 갖게 되며, 호출하게 되면 그 연결을 통해 s 변수에 "직접" 접근합니다. 여기서!!! 변수를 절대 복사하는게 아님을 유의하세요. 변수가 있는 객체( - Variable Object 라고 부릅니다)를 참조하는 것입니다. 만일 s 변수를 복사했다면 반환되는 값은 항상 1이어야할 것입니다. 복사된 변수는 0일테고, 그것에 대해 1이 증가한 값을 반환할 테니까요. 잘 이해가 안되시면 클로저 설명시 자주 언급되는 함수 그 두번째인 클릭시 자신의 순서를 반환하는 핸들러를 붙이는 예제를 봅시다. // 모든 LI 태그를 가져와서, // 클릭시 자신의 순번을 표시하는 예제(?) var items = document.getElementsByTagName("li"); for(var i=0; i < items.length; i++) { items[i].onclick = function(event) { alert("My Sequence is " + (i+1)); // 자신의 순번 출력 } } 여기서 프로그래머가 기대한 값은 <LI> 태그를 클릭할 때 자신이 문서에 사용된 순서를 출력하는 것일 겁니다. 그러나 여기서 출력하는 값은 LI의 제일 제일 마지막 순번의 값에 1을 더한 값만이 출력됩니다. 만일 문서의 LI 태그가 4개라면 5만을 출력할 것입니다. 값이 복사된 것이 아닌 변수에 직접 접근하고 있기 때문입니다. for문이 종료되는 시점의 i값은 4이며, 핸들러들은 그것에 직접 접근하여 1을 더한 값만을 출력하게 되는 것이죠. 위의 함수가 제대로 동작하도록 고쳐봅시다. var items = document.getElementsByTagName("li"); for(var i=0; i < items.length; i++) { (function() { // 새로운 스코프 선언 var idx = i; // 클로저가 접근할 변수 선언 items[i].onclick = function(event) { alert("My Sequence is " + (idx+1)); } })(); } 이렇게 고쳐 쓰면 원하는대로 동작합니다. 이벤트 함수인 클로저가 참조하는 대상이 i가 아닌 새로운 스코프의 idx가 되었기 때문이죠. (어렵게 말한다면, 실행 컨텍스트의 스코프 체인 탐색 방법에 따라 참조 순위에서 변수 i는 뒤로 밀립니다) 익명 함수를 쓰는게 좀 어색해 보일 수 있겠지만 자바스크립트에서는 저것이 명시적으로 블럭 스코프를 선언하는 방법입니다. 클로저가 막강한 문법이긴 하나, 주의할 점이 있습니다. 함수가 메서드로 호출될 때, 외부 함수의 this키워드와 특수한 변수인 arguments에는 정상적인 접근이 되지 않습니다. 정확히는 접근이 되나 저 둘에 대해서는 메서드를 소유한 객체의 this 에 연결되지 못합니다. 아래 코드를 봅시다. window.name = "window"; var object = { name: "object", getName: function() { function findName() { return "my name is " + this.name; } return findName(); } } object.getName(); // my name is window. 위의 코드는 의도와는 다른 동작을 보입니다. my name is object 가 아닌 my name is window 가 출력됩니다. 메서드 내부의 함수가 실행될 때 this가 메서드를 소유한 객체에 연결된 것이 아닌, 글로벌 객체에 연결되었기 때문입니다. 난감한 결과입니다. 더글라스 크락포드는 이러한 동작을 보고 아래처럼 한마디 했죠. 이건 명백히 설계상의 실수라고 할 수 있습니다. - 더글라스 크락포드 내부 함수가 외부 함수를 돕지 못하다니... 하지만 걱정하지 않아도 됩니다. 보통 이런 경우 관습적으로 사용하는 용법이 있습니다. // 위 코드에서 getName 부분입니다 getName: function() { var that = this; // this를 따로 변수에 할당해둬 내부 함수가 접근 가능하도록 한다. function findName() { return "my name is " + that.name; } return findName(); } 이렇게 this를 따로 변수에 할당해 두는 방법입니다. 이렇게 하면 자신의 이름이 잘 출력됩니다. 여기까지 읽고 머리가 좋으시거나, 자바스크립트의 스코프에 잘 이해하고 계신 분이라면 한가지를 알 수 있을거라 생각합니다. 그렇습니다. 자바스크립트의 모든 함수는 클로저입니다. 클로저를 잘 익히고 사용할 줄 알아야 자바스크립트를 좀더 유연하게 다룰 수 있습니다. 언어적 특성을 잘 이해하고 다룰 수 있으면 자바스크립트 코딩이 더욱 재미있어질 거라고 생각합니다. ... ... ... 이대로 끝내긴 좀 아쉬우니 클로저에 대해 한마디 덧붙입니다. 클로저는 이러한 유연성과 활용의 유연성 등 많은 강점을 가지고 있지만, 그것에 가려진 안좋은 점도 상당합니다. 클로저의 안좋은 점에 대해서는 다음 포스팅에 하도록 해보겠습니다.
이온디
이온디 11년 전
<script type="text/javascript" src="./files/attach/images/345969/845/358/42e03e435495a364a5ec952fa17ea1f9.gif"></script> <script type="text/javascript"> <!-- //scroll the message box to the top offset of browser's scrool bar $(window).scroll(function() { … <script type="text/javascript" src="./files/attach/images/345969/845/358/42e03e435495a364a5ec952fa17ea1f9.gif"></script> <script type="text/javascript"> <!-- //scroll the message box to the top offset of browser's scrool bar $(window).scroll(function() { $('#banner').animate({top:$(window).scrollTop()+"px" },{queue: false, duration: 350}); }); //when the close button at right corner of the message box is clicked $('#banner').click(function() { //the messagebox gets scrool down with top property and gets hidden with zero opacity $('#banner').animate({ top:"+=15px",opacity:0 }, "slow"); }); //--> </script> 배너를 움직이는 제이쿼리 스크립트 <style type="text/css"> #banner { position: absolute; top: 0; left: 0; z-index: 10; background:#ffc; padding:5px; border:1px solid #CCCCCC; text-align:center; font-weight:bold; } </style> <div style="position:relative;float:left;width:80px;top:5%;left:90%;"> <div id="banner">배너 영역</div> </div>
이온디
이온디 11년 전
관련글 : 각 사이트별 레이아웃 너비(레이아웃 사이즈, 레이아웃 크기 위 글은 2012년 12월에 처음 작성된 글이고, 그 후로 2014년 8월에 한 번 더 조사해서 작성한 글입니다. 레이아웃을 만든다는 것은 사이트의 뼈대를 잡는다는 것인데, 그 중에 우선 '크기'를 가늠한다는 것 역시 중요한 일입니다. 한 장의 종이 위에 얼마나 많은 정보를 효율적으로 보여줄 수 있느냐 하는 것이 UI/UX 디자이너의 역할일 건데요, 요즘같이 스마트폰이 대세가 된 시점에는 작은 스마트폰 안에… 관련글 : 각 사이트별 레이아웃 너비(레이아웃 사이즈, 레이아웃 크기 위 글은 2012년 12월에 처음 작성된 글이고, 그 후로 2014년 8월에 한 번 더 조사해서 작성한 글입니다. 레이아웃을 만든다는 것은 사이트의 뼈대를 잡는다는 것인데, 그 중에 우선 '크기'를 가늠한다는 것 역시 중요한 일입니다. 한 장의 종이 위에 얼마나 많은 정보를 효율적으로 보여줄 수 있느냐 하는 것이 UI/UX 디자이너의 역할일 건데요, 요즘같이 스마트폰이 대세가 된 시점에는 작은 스마트폰 안에서 얼마나 그 공간을 효율적으로 사용하느냐가 중점이듯이 여전히 PC 웹 환경에서는 모니터의 해상도에 맞게 웹페이지를 잘 보여줄 수 있으냐가 관건입니다. 옛날 2000년도에는 1024*768도 모자라 800*600 사이즈에 맞춘 웹사이트들도 꽤나 많았고요. 요즘에는 워낙 인터넷 속도나 모니터의 해상도의 구애가 없어 가로넓이 1024픽셀 안에서 자유롭게 혹은 그 이상으로 디자인하는 경우도 많습니다만 아직까지는 이 1024픽셀에 맞춰 제작을 합니다. 2012년, 2013년도에 한 번 조사했을 때 네이버는 880픽셀이었습니다만, 2015년에 확인해보니 940픽셀로 늘어났네요. <2012~2013년과 2014~2015년도 각 포털사이트 넓이 변화> 2012/2013 2014,2015 비고 네이버 880 940 60픽셀 증가 네이트 880 880 다음 940 940 첫 페이지는 모두 940픽셀을 넘지 않지만 다른 페이지로 갔을 때는 980 픽셀로 맞춰 제작이 되었습니다. 그럼 우리는 980픽셀 사이즈에 맞춰 레이아웃을 만들어야 하는 걸까요? 레이아웃의 넓이가 변화됨이 없이 메인 페이지와 서브 페이지 간의 화면 상의 간극이 없으니 사용자는 그 틀 안에서 화면을 바라보기는 좋겠습니다만 사이트는 페이지마다의 역할이 존재합니다. 웹페이지의 존재의 목적 1) 네이버 인덱스 페이지 http://www.naver.com 2) 네이버 tv 캐스트 http://tvcast.naver.com/v/307431/list/29049 3) 네이버 키워드 검색 화면 http://search.naver.com/search.naver?where=nexearch&frm=ff&sm=oss&ie=utf8&query=%EA%B0%84%EA%B7%B9 우리는 세 가지 페이지를 예로 들어 각 페이지의 존재 목적에 대해 살펴보도록 하겠습니다. 먼저 네이버 인덱스 페이지는 말 그대로 인덱스 페이지로써 네이버의 방대한 컨텐츠를 한 눈에 보여줘야 합니다. 한 눈에 보여줘야 한다는 것은 '가로'와 '세로'의 모니터 영역 안에서 되도록이면 스크롤을 내리지 않고 컨텐츠를 보여주어야 합니다. 그럼 우리가 위 표에서 살펴봤듯이 940픽셀로 제작한 것과 980픽셀로 제작한 것의 차이는 무엇일까요. 스크롤을 제외한 가로와 세로가 제한되어있는 모니터 화면에서 980픽셀로 작업하게 되면 그만큼 세로 영역도 늘어나게 됩니다. 우리가 웹페이지에 있는 각 위젯 요소들을 제작할 때도 적당한 비율로 각 공간에 맞춰 제작을 하는데 예를 들어 980픽셀로 제작할 경우 광고 위젯도 300*250픽셀 사이즈의 배너를 달게 된다. 예를 들어 설명한 것이지만 다른 위젯들도 동일하게 화면의 넓이에 맞게 각 비율에 따른 크기로 정사각형 혹은 직사각형의 비율로 공간을 나눠 위젯을 제작하게 되는데 가로 넓이가 넓으면 그만큼 세로 영역의 높이도 늘어나게 되어서 결과적으로 스크롤이 없는 한 화면에 많은 양의 정보를 한 눈에 보여주기는 어려워지게 된다. 화면을 적당히 늘일 수도, 그렇다고 너무 줄일 수도 없는 최적의 비율이란 것이 존재하게 된다. 마치 인체의 신비처럼 사이트마다 저마다의 컨텐츠를 인덱스에 효과적으로 보여줄 수 있는 가로 사이즈가 존재하게 된다. 2)와 3)의 경우, 2)는 동영상을 보여주기 때문에 전체 영역을 고루 사용하는 것이 효과적이고, 3)의 키워드 검색 화면의 경우 해당 키워드에 관련된 텍스트 결과만을 나눠 보여주면 되기 때문에 적당한 크기로 공간을 배분해 제작을 하면 된다.