최초커밋
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="output" path="WebContent/WEB-INF/classes"/>
|
||||
</classpath>
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# 아키텍처 가이드
|
||||
|
||||
## 전체 아키텍처
|
||||
|
||||
이 애플리케이션은 전형적인 Spring MVC 3-tier 아키텍처를 따릅니다:
|
||||
|
||||
- **Presentation Layer**: JSP + jQuery (Frontend)
|
||||
- **Business Layer**: Spring Controllers + Services (Backend Logic)
|
||||
- **Data Access Layer**: MyBatis + PostgreSQL (Database)
|
||||
|
||||
## 패키지 구조
|
||||
|
||||
```
|
||||
src/com/pms/
|
||||
├── controller/ # Spring MVC Controllers (@Controller)
|
||||
├── service/ # Business Logic (@Service)
|
||||
├── mapper/ # MyBatis XML Mappers
|
||||
├── common/ # 공통 유틸리티 및 설정
|
||||
├── salesmgmt/ # 영업관리 모듈
|
||||
└── ions/ # 특수 모듈
|
||||
```
|
||||
|
||||
## 주요 컴포넌트
|
||||
|
||||
### Controllers
|
||||
|
||||
모든 컨트롤러는 [BaseService](mdc:src/com/pms/common/service/BaseService.java)를 상속받습니다.
|
||||
|
||||
- URL 패턴: `*.do` (예: `/admin/menuMngList.do`)
|
||||
- 주요 컨트롤러: [AdminController](mdc:src/com/pms/controller/AdminController.java)
|
||||
|
||||
### Services
|
||||
|
||||
비즈니스 로직을 처리하는 서비스 계층입니다.
|
||||
|
||||
- 예시: [AdminService](mdc:src/com/pms/service/AdminService.java)
|
||||
- MyBatis SqlSession을 직접 사용하여 데이터베이스 접근
|
||||
|
||||
### MyBatis Mappers
|
||||
|
||||
SQL 쿼리를 XML로 정의합니다.
|
||||
|
||||
- 위치: `src/com/pms/mapper/`
|
||||
- 예시: [admin.xml](mdc:src/com/pms/mapper/admin.xml)
|
||||
|
||||
### JSP Views
|
||||
|
||||
JSP 뷰 파일들은 `WebContent/WEB-INF/view/` 디렉토리에 위치합니다.
|
||||
|
||||
- InternalResourceViewResolver 사용
|
||||
- prefix: `/WEB-INF/view`, suffix: `.jsp`
|
||||
|
||||
## 데이터베이스 설정
|
||||
|
||||
- JNDI DataSource 사용: `plm`
|
||||
- PostgreSQL 연결
|
||||
- 초기 데이터: [ilshin.pgsql](mdc:db/ilshin.pgsql)
|
||||
|
||||
## 설정 파일 위치
|
||||
|
||||
- Spring 설정: [dispatcher-servlet.xml](mdc:WebContent/WEB-INF/dispatcher-servlet.xml)
|
||||
- 로깅 설정: [log4j.xml](mdc:WebContent/WEB-INF/log4j.xml)
|
||||
- 웹 설정: [web.xml](mdc:WebContent/WEB-INF/web.xml)
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# 데이터베이스 가이드
|
||||
|
||||
## 데이터베이스 설정
|
||||
|
||||
### PostgreSQL 연결
|
||||
- **JNDI 리소스명**: `plm`
|
||||
- **드라이버**: `org.postgresql.Driver`
|
||||
- **설정 파일**: [context.xml](mdc:tomcat-conf/context.xml)
|
||||
|
||||
### 초기 데이터
|
||||
- **스키마 파일**: [ilshin.pgsql](mdc:db/ilshin.pgsql)
|
||||
- **역할 설정**: [00-create-roles.sh](mdc:db/00-create-roles.sh)
|
||||
|
||||
## MyBatis 설정
|
||||
|
||||
### SqlSession 사용 패턴
|
||||
```java
|
||||
public List<Map<String, Object>> getData(Map<String, Object> paramMap) {
|
||||
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession();
|
||||
try {
|
||||
return sqlSession.selectList("namespace.queryId", paramMap);
|
||||
} finally {
|
||||
sqlSession.close(); // 반드시 리소스 해제
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 트랜잭션 처리
|
||||
```java
|
||||
public void saveData(Map<String, Object> paramMap) {
|
||||
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession(false); // autoCommit=false
|
||||
try {
|
||||
sqlSession.insert("namespace.insertQuery", paramMap);
|
||||
sqlSession.update("namespace.updateQuery", paramMap);
|
||||
sqlSession.commit(); // 명시적 커밋
|
||||
} catch (Exception e) {
|
||||
sqlSession.rollback(); // 오류 시 롤백
|
||||
throw e;
|
||||
} finally {
|
||||
sqlSession.close();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 매퍼 XML 작성 가이드
|
||||
|
||||
### 기본 구조
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="admin">
|
||||
<!-- 쿼리 정의 -->
|
||||
</mapper>
|
||||
```
|
||||
|
||||
### 파라미터 바인딩
|
||||
```xml
|
||||
<!-- 안전한 파라미터 바인딩 (권장) -->
|
||||
<select id="selectUser" parameterType="map" resultType="map">
|
||||
SELECT * FROM users
|
||||
WHERE user_id = #{userId}
|
||||
AND status = #{status}
|
||||
</select>
|
||||
|
||||
<!-- 동적 조건 처리 -->
|
||||
<select id="selectUserList" parameterType="map" resultType="map">
|
||||
SELECT * FROM users
|
||||
WHERE 1=1
|
||||
<if test="userName != null and userName != ''">
|
||||
AND user_name LIKE '%' || #{userName} || '%'
|
||||
</if>
|
||||
<if test="deptCode != null and deptCode != ''">
|
||||
AND dept_code = #{deptCode}
|
||||
</if>
|
||||
</select>
|
||||
```
|
||||
|
||||
### PostgreSQL 특화 문법
|
||||
```xml
|
||||
<!-- 시퀀스 사용 -->
|
||||
<insert id="insertData" parameterType="map">
|
||||
INSERT INTO table_name (id, name, reg_date)
|
||||
VALUES (nextval('seq_table'), #{name}, now())
|
||||
</insert>
|
||||
|
||||
<!-- 숫자 타입 캐스팅 -->
|
||||
<update id="updateData" parameterType="map">
|
||||
UPDATE table_name
|
||||
SET status = #{status}
|
||||
WHERE id = #{id}::numeric
|
||||
</update>
|
||||
|
||||
<!-- 재귀 쿼리 (메뉴 트리 구조) -->
|
||||
<select id="selectMenuTree" resultType="map">
|
||||
WITH RECURSIVE menu_tree AS (
|
||||
SELECT * FROM menu_info WHERE parent_id = 0
|
||||
UNION ALL
|
||||
SELECT m.* FROM menu_info m
|
||||
JOIN menu_tree mt ON m.parent_id = mt.id
|
||||
)
|
||||
SELECT * FROM menu_tree ORDER BY path
|
||||
</select>
|
||||
```
|
||||
|
||||
## 주요 테이블 구조
|
||||
|
||||
### 메뉴 관리
|
||||
- **MENU_INFO**: 메뉴 정보
|
||||
- **MENU_AUTH_GROUP**: 메뉴 권한 그룹
|
||||
- **AUTH_GROUP**: 권한 그룹 정보
|
||||
|
||||
### 사용자 관리
|
||||
- **USER_INFO**: 사용자 정보
|
||||
- **DEPT_INFO**: 부서 정보
|
||||
- **USER_AUTH**: 사용자 권한
|
||||
|
||||
### 코드 관리
|
||||
- **CODE_INFO**: 공통 코드
|
||||
- **CODE_CATEGORY**: 코드 카테고리
|
||||
|
||||
## 데이터베이스 개발 모범 사례
|
||||
|
||||
### 1. 파라미터 검증
|
||||
```xml
|
||||
<select id="selectData" parameterType="map" resultType="map">
|
||||
SELECT * FROM table_name
|
||||
WHERE 1=1
|
||||
<if test="id != null and id != ''">
|
||||
AND id = #{id}::numeric
|
||||
</if>
|
||||
</select>
|
||||
```
|
||||
|
||||
### 2. 페이징 처리
|
||||
```xml
|
||||
<select id="selectListWithPaging" parameterType="map" resultType="map">
|
||||
SELECT * FROM (
|
||||
SELECT *, ROW_NUMBER() OVER (ORDER BY reg_date DESC) as rnum
|
||||
FROM table_name
|
||||
WHERE 1=1
|
||||
<!-- 검색 조건 -->
|
||||
) t
|
||||
WHERE rnum BETWEEN #{startRow}::numeric AND #{endRow}::numeric
|
||||
</select>
|
||||
```
|
||||
|
||||
### 3. 대소문자 처리
|
||||
```xml
|
||||
<!-- PostgreSQL은 대소문자를 구분하므로 주의 -->
|
||||
<select id="selectData" resultType="map">
|
||||
SELECT
|
||||
user_id as "userId", -- 카멜케이스 변환
|
||||
user_name as "userName",
|
||||
UPPER(status) as "status"
|
||||
FROM user_info
|
||||
</select>
|
||||
```
|
||||
|
||||
### 4. NULL 처리
|
||||
```xml
|
||||
<select id="selectData" resultType="map">
|
||||
SELECT
|
||||
COALESCE(description, '') as description,
|
||||
CASE WHEN status = 'Y' THEN '활성' ELSE '비활성' END as statusName
|
||||
FROM table_name
|
||||
</select>
|
||||
```
|
||||
|
||||
## 성능 최적화
|
||||
|
||||
### 인덱스 활용
|
||||
```sql
|
||||
-- 자주 검색되는 컬럼에 인덱스 생성
|
||||
CREATE INDEX idx_user_dept ON user_info(dept_code);
|
||||
CREATE INDEX idx_menu_parent ON menu_info(parent_id);
|
||||
```
|
||||
|
||||
### 쿼리 최적화
|
||||
```xml
|
||||
<!-- EXISTS 사용으로 성능 개선 -->
|
||||
<select id="selectUserWithAuth" resultType="map">
|
||||
SELECT u.* FROM user_info u
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM user_auth ua
|
||||
WHERE ua.user_id = u.user_id
|
||||
AND ua.auth_code = #{authCode}
|
||||
)
|
||||
</select>
|
||||
```
|
||||
|
||||
### 배치 처리
|
||||
```xml
|
||||
<!-- 대량 데이터 삽입 시 배치 사용 -->
|
||||
<insert id="insertBatch" parameterType="list">
|
||||
INSERT INTO table_name (col1, col2, col3)
|
||||
VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.col1}, #{item.col2}, #{item.col3})
|
||||
</foreach>
|
||||
</insert>
|
||||
```
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# 개발 가이드
|
||||
|
||||
## 개발 환경 설정
|
||||
|
||||
### Docker 개발 환경
|
||||
```bash
|
||||
# 개발 환경 실행
|
||||
docker-compose -f docker-compose.dev.yml up --build -d
|
||||
|
||||
# 운영 환경 실행
|
||||
docker-compose -f docker-compose.prod.yml up --build -d
|
||||
```
|
||||
|
||||
### 로컬 개발 환경
|
||||
1. Java 7 JDK 설치
|
||||
2. Eclipse IDE 설정
|
||||
3. Tomcat 7.0 설정
|
||||
4. PostgreSQL 데이터베이스 설정
|
||||
|
||||
## 코딩 컨벤션
|
||||
|
||||
### Controller 개발
|
||||
```java
|
||||
@Controller
|
||||
public class ExampleController extends BaseService {
|
||||
|
||||
@Autowired
|
||||
ExampleService exampleService;
|
||||
|
||||
@RequestMapping("/example/list.do")
|
||||
public String getList(HttpServletRequest request,
|
||||
@RequestParam Map<String, Object> paramMap) {
|
||||
// 비즈니스 로직은 Service에서 처리
|
||||
List<Map<String, Object>> list = exampleService.getList(request, paramMap);
|
||||
request.setAttribute("list", list);
|
||||
return "/example/list";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Service 개발
|
||||
```java
|
||||
@Service
|
||||
public class ExampleService extends BaseService {
|
||||
|
||||
public List<Map<String, Object>> getList(HttpServletRequest request,
|
||||
Map<String, Object> paramMap) {
|
||||
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession();
|
||||
try {
|
||||
return sqlSession.selectList("example.selectList", paramMap);
|
||||
} finally {
|
||||
sqlSession.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MyBatis Mapper 개발
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="example">
|
||||
<select id="selectList" parameterType="map" resultType="map">
|
||||
SELECT * FROM example_table
|
||||
WHERE 1=1
|
||||
<if test="searchText != null and searchText != ''">
|
||||
AND name LIKE '%' || #{searchText} || '%'
|
||||
</if>
|
||||
</select>
|
||||
</mapper>
|
||||
```
|
||||
|
||||
## 주요 유틸리티
|
||||
|
||||
### 공통 유틸리티
|
||||
- [CommonUtils](mdc:src/com/pms/common/utils/CommonUtils.java) - 공통 유틸리티 메서드
|
||||
- [Constants](mdc:src/com/pms/common/utils/Constants.java) - 상수 정의
|
||||
- [Message](mdc:src/com/pms/common/Message.java) - 메시지 처리
|
||||
|
||||
### 파일 관련
|
||||
- [FileRenameClass](mdc:src/com/pms/common/FileRenameClass.java) - 파일명 변경
|
||||
- 파일 업로드/다운로드 처리
|
||||
|
||||
## 프론트엔드 개발
|
||||
|
||||
### JSP 개발
|
||||
- 위치: `WebContent/WEB-INF/view/`
|
||||
- 공통 초기화: [init_jqGrid.jsp](mdc:WebContent/init_jqGrid.jsp)
|
||||
- 스타일시트: [all.css](mdc:WebContent/css/all.css)
|
||||
|
||||
### JavaScript 라이브러리
|
||||
- jQuery 1.11.3/2.1.4
|
||||
- jqGrid 4.7.1 - 데이터 그리드
|
||||
- Tabulator - 테이블 컴포넌트
|
||||
- rMateChart - 차트 라이브러리
|
||||
|
||||
## 데이터베이스 개발
|
||||
|
||||
### 연결 설정
|
||||
- JNDI 리소스명: `plm`
|
||||
- 드라이버: PostgreSQL
|
||||
- 컨텍스트 설정: [context.xml](mdc:tomcat-conf/context.xml)
|
||||
|
||||
### 스키마 관리
|
||||
- 초기 스키마: [ilshin.pgsql](mdc:db/ilshin.pgsql)
|
||||
- 역할 설정: [00-create-roles.sh](mdc:db/00-create-roles.sh)
|
||||
|
||||
## 빌드 및 배포
|
||||
- Eclipse 기반 빌드 (Maven/Gradle 미사용)
|
||||
- 컴파일된 클래스: `WebContent/WEB-INF/classes/`
|
||||
- 라이브러리: `WebContent/WEB-INF/lib/`
|
||||
- WAR 파일로 Tomcat 배포
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# Next.js 마이그레이션 가이드
|
||||
|
||||
## 마이그레이션 개요
|
||||
현재 JSP/jQuery 기반 프론트엔드를 Next.js로 전환하는 작업이 계획되어 있습니다.
|
||||
자세한 내용은 [TODO.md](mdc:TODO.md)를 참조하세요.
|
||||
|
||||
## 현재 프론트엔드 분석
|
||||
|
||||
### JSP 뷰 구조
|
||||
```
|
||||
WebContent/WEB-INF/view/
|
||||
├── admin/ # 관리자 화면
|
||||
├── approval/ # 승인 관리
|
||||
├── common/ # 공통 컴포넌트
|
||||
├── dashboard/ # 대시보드
|
||||
├── main/ # 메인 화면
|
||||
└── ... # 기타 모듈별 화면
|
||||
```
|
||||
|
||||
### 주요 JavaScript 라이브러리
|
||||
- **jQuery**: 1.11.3/2.1.4 - DOM 조작 및 AJAX
|
||||
- **jqGrid**: 4.7.1 - 데이터 그리드 (교체 필요)
|
||||
- **Tabulator**: 테이블 컴포넌트
|
||||
- **rMateChart**: 차트 라이브러리 (교체 필요)
|
||||
- **CKEditor**: 텍스트 에디터
|
||||
|
||||
### CSS 프레임워크
|
||||
- [all.css](mdc:WebContent/css/all.css) - 메인 스타일시트
|
||||
- jQuery UI 테마 적용
|
||||
- 반응형 디자인 미적용 (데스크톱 중심)
|
||||
|
||||
## API 설계 가이드
|
||||
|
||||
### RESTful API 변환
|
||||
현재 Spring MVC는 JSP 뷰를 반환하는 구조입니다:
|
||||
```java
|
||||
@RequestMapping("/admin/menuMngList.do")
|
||||
public String getMenuList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
|
||||
// 데이터 조회
|
||||
List<Map<String, Object>> menuList = adminService.getMenuList(request, paramMap);
|
||||
request.setAttribute("menuList", menuList);
|
||||
return "/admin/menu/menuMngList"; // JSP 뷰 반환
|
||||
}
|
||||
```
|
||||
|
||||
Next.js 연동을 위해 JSON API로 변환 필요:
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class AdminApiController {
|
||||
|
||||
@GetMapping("/admin/menus")
|
||||
@ResponseBody
|
||||
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getMenuList(
|
||||
@RequestParam Map<String, Object> paramMap) {
|
||||
List<Map<String, Object>> menuList = adminService.getMenuList(null, paramMap);
|
||||
return ResponseEntity.ok(ApiResponse.success(menuList));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### API 응답 표준화
|
||||
```java
|
||||
public class ApiResponse<T> {
|
||||
private boolean success;
|
||||
private T data;
|
||||
private String message;
|
||||
private String errorCode;
|
||||
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
return new ApiResponse<>(true, data, null, null);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> error(String message, String errorCode) {
|
||||
return new ApiResponse<>(false, null, message, errorCode);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 컴포넌트 매핑 가이드
|
||||
|
||||
### 데이터 그리드 교체
|
||||
**현재**: jqGrid 4.7.1
|
||||
```javascript
|
||||
$("#grid").jqGrid({
|
||||
url: 'menuMngList.do',
|
||||
datatype: 'json',
|
||||
colModel: [
|
||||
{name: 'menuName', label: '메뉴명'},
|
||||
{name: 'url', label: 'URL'}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
**변환 후**: TanStack Table 또는 AG Grid
|
||||
```tsx
|
||||
import { useTable } from '@tanstack/react-table';
|
||||
|
||||
const MenuTable = () => {
|
||||
const columns = [
|
||||
{ accessorKey: 'menuName', header: '메뉴명' },
|
||||
{ accessorKey: 'url', header: 'URL' }
|
||||
];
|
||||
|
||||
const table = useTable({ data, columns });
|
||||
// 테이블 렌더링
|
||||
};
|
||||
```
|
||||
|
||||
### 차트 라이브러리 교체
|
||||
**현재**: rMateChart
|
||||
**변환 후**: Recharts 또는 Chart.js
|
||||
|
||||
### 폼 처리 교체
|
||||
**현재**: jQuery 기반 폼 처리
|
||||
**변환 후**: react-hook-form 사용
|
||||
|
||||
## 인증/인가 처리
|
||||
|
||||
### 현재 세션 기반 인증
|
||||
```java
|
||||
// 세션에서 사용자 정보 조회
|
||||
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
|
||||
```
|
||||
|
||||
### Next.js 연동 방안
|
||||
1. **세션 유지**: 쿠키 기반 세션 ID 전달
|
||||
2. **JWT 토큰**: 새로운 토큰 기반 인증 도입
|
||||
3. **하이브리드**: 기존 세션 + API 토큰
|
||||
|
||||
## 개발 단계별 접근
|
||||
|
||||
### Phase 1: API 개발
|
||||
1. 기존 Controller 분석
|
||||
2. @RestController 신규 생성
|
||||
3. 기존 Service 재사용
|
||||
4. CORS 설정 추가
|
||||
|
||||
### Phase 2: Next.js 기본 구조
|
||||
1. Next.js 프로젝트 생성
|
||||
2. 기본 레이아웃 구현
|
||||
3. 라우팅 구조 설계
|
||||
4. 공통 컴포넌트 개발
|
||||
|
||||
### Phase 3: 화면별 마이그레이션
|
||||
1. 관리자 화면부터 시작
|
||||
2. 주요 업무 화면 순차 전환
|
||||
3. 대시보드 및 리포트 화면
|
||||
|
||||
### Phase 4: 테스트 및 최적화
|
||||
1. 기능 테스트
|
||||
2. 성능 최적화
|
||||
3. 사용자 테스트
|
||||
4. 점진적 배포
|
||||
|
||||
## 주의사항
|
||||
|
||||
### 데이터 호환성
|
||||
- 기존 데이터베이스 스키마 유지
|
||||
- API 응답 형식 표준화
|
||||
- 날짜/시간 형식 통일
|
||||
|
||||
### 사용자 경험
|
||||
- 기존 업무 프로세스 유지
|
||||
- 화면 전환 시 혼란 최소화
|
||||
- 점진적 마이그레이션 고려
|
||||
|
||||
### 성능 고려사항
|
||||
- API 응답 속도 최적화
|
||||
- 클라이언트 사이드 캐싱
|
||||
- 이미지 및 정적 자원 최적화
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# 네비게이션 가이드
|
||||
|
||||
## 프로젝트 구조 이해
|
||||
|
||||
### 루트 디렉토리
|
||||
```
|
||||
plm-ilshin/
|
||||
├── src/ # Java 소스 코드
|
||||
├── WebContent/ # 웹 리소스 (JSP, CSS, JS, 이미지)
|
||||
├── db/ # 데이터베이스 스크립트
|
||||
├── tomcat-conf/ # Tomcat 설정
|
||||
├── docker-compose.*.yml # Docker 설정
|
||||
└── 문서/ # 프로젝트 문서
|
||||
```
|
||||
|
||||
### 주요 소스 디렉토리
|
||||
```
|
||||
src/com/pms/
|
||||
├── controller/ # 웹 컨트롤러 (@Controller)
|
||||
├── service/ # 비즈니스 로직 (@Service)
|
||||
├── mapper/ # MyBatis SQL 매퍼 (XML)
|
||||
├── common/ # 공통 컴포넌트
|
||||
├── salesmgmt/ # 영업관리 모듈
|
||||
└── ions/ # 특수 기능 모듈
|
||||
```
|
||||
|
||||
### 웹 리소스 구조
|
||||
```
|
||||
WebContent/
|
||||
├── WEB-INF/
|
||||
│ ├── view/ # JSP 뷰 파일
|
||||
│ ├── lib/ # JAR 라이브러리
|
||||
│ ├── classes/ # 컴파일된 클래스
|
||||
│ └── *.xml # 설정 파일
|
||||
├── css/ # 스타일시트
|
||||
├── js/ # JavaScript 파일
|
||||
├── images/ # 이미지 리소스
|
||||
└── template/ # 템플릿 파일
|
||||
```
|
||||
|
||||
## 주요 파일 찾기
|
||||
|
||||
### 컨트롤러 찾기
|
||||
특정 URL에 대한 컨트롤러를 찾을 때:
|
||||
1. URL 패턴 확인 (예: `/admin/menuMngList.do`)
|
||||
2. `src/com/pms/controller/` 에서 해당 `@RequestMapping` 검색
|
||||
3. 주요 컨트롤러들:
|
||||
- [AdminController.java](mdc:src/com/pms/controller/AdminController.java) - 관리자 기능
|
||||
- [ApprovalController.java](mdc:src/com/pms/controller/ApprovalController.java) - 승인 관리
|
||||
- [AsController.java](mdc:src/com/pms/controller/AsController.java) - AS 관리
|
||||
|
||||
### 서비스 찾기
|
||||
비즈니스 로직을 찾을 때:
|
||||
1. 컨트롤러에서 `@Autowired` 된 서비스 확인
|
||||
2. `src/com/pms/service/` 디렉토리에서 해당 서비스 파일 찾기
|
||||
3. 주요 서비스들:
|
||||
- [AdminService.java](mdc:src/com/pms/service/AdminService.java) - 관리자 서비스
|
||||
- [ApprovalService.java](mdc:src/com/pms/service/ApprovalService.java) - 승인 서비스
|
||||
|
||||
### SQL 쿼리 찾기
|
||||
데이터베이스 쿼리를 찾을 때:
|
||||
1. 서비스 코드에서 `sqlSession.selectList("namespace.queryId")` 확인
|
||||
2. `src/com/pms/mapper/` 에서 해당 namespace XML 파일 찾기
|
||||
3. XML 파일 내에서 queryId로 검색
|
||||
|
||||
### JSP 뷰 찾기
|
||||
화면을 찾을 때:
|
||||
1. 컨트롤러 메서드의 return 값 확인 (예: `"/admin/menu/menuMngList"`)
|
||||
2. `WebContent/WEB-INF/view/` + return 값 + `.jsp` 경로로 파일 찾기
|
||||
|
||||
## 모듈별 주요 기능
|
||||
|
||||
### 관리자 모듈 (`/admin/*`)
|
||||
- 메뉴 관리: [AdminController.java](mdc:src/com/pms/controller/AdminController.java)
|
||||
- 사용자 관리, 권한 관리
|
||||
- 코드 관리, 카테고리 관리
|
||||
- 시스템 로그 관리
|
||||
|
||||
### 영업 관리 (`/salesmgmt/*`)
|
||||
- 위치: `src/com/pms/salesmgmt/`
|
||||
- 영업 관련 컨트롤러, 서비스, 매퍼 분리
|
||||
|
||||
### 공통 기능 (`/common/*`)
|
||||
- 공통 유틸리티: [CommonUtils](mdc:src/com/pms/common/utils/CommonUtils.java)
|
||||
- 메시지 처리: [Message](mdc:src/com/pms/common/Message.java)
|
||||
- 파일 처리: [FileRenameClass](mdc:src/com/pms/common/FileRenameClass.java)
|
||||
|
||||
## 개발 시 주의사항
|
||||
|
||||
### 파일 수정 시
|
||||
1. Java 파일 수정 → Eclipse에서 자동 컴파일 → `WebContent/WEB-INF/classes/`에 반영
|
||||
2. JSP/CSS/JS 수정 → 바로 반영 (서버 재시작 불필요)
|
||||
3. XML 설정 파일 수정 → 서버 재시작 필요
|
||||
|
||||
### 데이터베이스 관련
|
||||
1. 스키마 변경 시 [ilshin.pgsql](mdc:db/ilshin.pgsql) 업데이트
|
||||
2. 새로운 쿼리 추가 시 해당 mapper XML 파일에 추가
|
||||
3. 트랜잭션 처리는 서비스 레벨에서 관리
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# PLM 솔루션 (ILSHIN) - 프로젝트 개요
|
||||
|
||||
## 프로젝트 정보
|
||||
이 프로젝트는 제품 수명 주기 관리(PLM - Product Lifecycle Management) 솔루션입니다.
|
||||
Spring Framework 기반의 Java 웹 애플리케이션으로, 제품 개발부터 폐기까지의 전체 생명주기를 관리합니다.
|
||||
|
||||
## 기술 스택
|
||||
- **Backend**: Java 7, Spring Framework 3.2.4, MyBatis 3.2.3
|
||||
- **Frontend**: JSP, jQuery 1.11.3/2.1.4, jqGrid 4.7.1
|
||||
- **Database**: PostgreSQL
|
||||
- **WAS**: Apache Tomcat 7.0
|
||||
- **Build**: Eclipse IDE 기반 (Maven/Gradle 미사용)
|
||||
|
||||
## 주요 기능
|
||||
- 제품 정보 관리
|
||||
- BOM (Bill of Materials) 관리
|
||||
- 설계 변경 관리 (ECO/ECR)
|
||||
- 문서 관리 및 버전 제어
|
||||
- 프로젝트/일정 관리
|
||||
- 사용자 및 권한 관리
|
||||
- 워크플로우 관리
|
||||
|
||||
## 주요 설정 파일
|
||||
- [web.xml](mdc:WebContent/WEB-INF/web.xml) - 웹 애플리케이션 배포 설정
|
||||
- [dispatcher-servlet.xml](mdc:WebContent/WEB-INF/dispatcher-servlet.xml) - Spring MVC 설정
|
||||
- [docker-compose.dev.yml](mdc:docker-compose.dev.yml) - 개발환경 Docker 설정
|
||||
- [docker-compose.prod.yml](mdc:docker-compose.prod.yml) - 운영환경 Docker 설정
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# 보안 가이드
|
||||
|
||||
## 인증 및 인가
|
||||
|
||||
### 세션 기반 인증
|
||||
현재 시스템은 세션 기반 인증을 사용합니다:
|
||||
|
||||
```java
|
||||
// 사용자 인증 정보 저장
|
||||
PersonBean person = new PersonBean();
|
||||
person.setUserId(userId);
|
||||
person.setUserName(userName);
|
||||
request.getSession().setAttribute(Constants.PERSON_BEAN, person);
|
||||
|
||||
// 인증 정보 조회
|
||||
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
|
||||
if (person == null) {
|
||||
// 로그인 페이지로 리다이렉트
|
||||
}
|
||||
```
|
||||
|
||||
### 권한 관리 구조
|
||||
- **AUTH_GROUP**: 권한 그룹 정의
|
||||
- **MENU_AUTH_GROUP**: 메뉴별 권한 그룹 매핑
|
||||
- **USER_AUTH**: 사용자별 권한 할당
|
||||
|
||||
### 메뉴 접근 권한 체크
|
||||
```java
|
||||
public HashMap<String, Object> checkUserMenuAuth(HttpServletRequest request, Map<String, Object> paramMap) {
|
||||
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
|
||||
String userId = person.getUserId();
|
||||
|
||||
paramMap.put("userId", userId);
|
||||
paramMap.put("menuUrl", request.getRequestURI());
|
||||
|
||||
// 권한 체크 쿼리 실행
|
||||
return sqlSession.selectOne("admin.checkUserMenuAuth", paramMap);
|
||||
}
|
||||
```
|
||||
|
||||
## 데이터 보안
|
||||
|
||||
### SQL 인젝션 방지
|
||||
**올바른 방법** - 파라미터 바인딩 사용:
|
||||
```xml
|
||||
<select id="selectUser" parameterType="map" resultType="map">
|
||||
SELECT * FROM user_info
|
||||
WHERE user_id = #{userId} <!-- 안전한 파라미터 바인딩 -->
|
||||
</select>
|
||||
```
|
||||
|
||||
**위험한 방법** - 직접 문자열 치환:
|
||||
```xml
|
||||
<select id="selectUser" parameterType="map" resultType="map">
|
||||
SELECT * FROM user_info
|
||||
WHERE user_id = '${userId}' <!-- SQL 인젝션 위험 -->
|
||||
</select>
|
||||
```
|
||||
|
||||
### 패스워드 보안
|
||||
```java
|
||||
// 패스워드 암호화 (EncryptUtil 사용)
|
||||
String encryptedPassword = EncryptUtil.encrypt(plainPassword);
|
||||
|
||||
// 패스워드 검증
|
||||
boolean isValid = EncryptUtil.matches(plainPassword, encryptedPassword);
|
||||
```
|
||||
|
||||
### 입력값 검증
|
||||
```java
|
||||
// CommonUtils를 사용한 입력값 정제
|
||||
String safeInput = CommonUtils.checkNull(request.getParameter("input"));
|
||||
if (StringUtils.isEmpty(safeInput)) {
|
||||
throw new IllegalArgumentException("필수 입력값이 누락되었습니다.");
|
||||
}
|
||||
```
|
||||
|
||||
## 세션 보안
|
||||
|
||||
### 세션 설정
|
||||
[web.xml](mdc:WebContent/WEB-INF/web.xml)에서 세션 타임아웃 설정:
|
||||
```xml
|
||||
<session-config>
|
||||
<session-timeout>1440</session-timeout> <!-- 24시간 -->
|
||||
</session-config>
|
||||
```
|
||||
|
||||
### 세션 무효화
|
||||
```java
|
||||
// 로그아웃 시 세션 무효화
|
||||
@RequestMapping("/logout.do")
|
||||
public String logout(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
session.invalidate();
|
||||
}
|
||||
return "redirect:/login.do";
|
||||
}
|
||||
```
|
||||
|
||||
## 파일 업로드 보안
|
||||
|
||||
### 파일 확장자 검증
|
||||
```java
|
||||
public boolean isAllowedFileType(String fileName) {
|
||||
String[] allowedExtensions = {".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx", ".xls", ".xlsx"};
|
||||
String extension = fileName.toLowerCase().substring(fileName.lastIndexOf("."));
|
||||
return Arrays.asList(allowedExtensions).contains(extension);
|
||||
}
|
||||
```
|
||||
|
||||
### 파일 크기 제한
|
||||
```java
|
||||
// web.xml에서 파일 업로드 크기 제한
|
||||
<multipart-config>
|
||||
<max-file-size>10485760</max-file-size> <!-- 10MB -->
|
||||
<max-request-size>52428800</max-request-size> <!-- 50MB -->
|
||||
</multipart-config>
|
||||
```
|
||||
|
||||
### 안전한 파일명 생성
|
||||
```java
|
||||
// FileRenameClass 사용하여 안전한 파일명 생성
|
||||
String safeFileName = FileRenameClass.rename(originalFileName);
|
||||
```
|
||||
|
||||
## XSS 방지
|
||||
|
||||
### 출력값 이스케이프
|
||||
JSP에서 사용자 입력값 출력 시:
|
||||
```jsp
|
||||
<!-- 안전한 출력 -->
|
||||
<c:out value="${userInput}" escapeXml="true"/>
|
||||
|
||||
<!-- 위험한 출력 -->
|
||||
${userInput} <!-- XSS 공격 가능 -->
|
||||
```
|
||||
|
||||
### JavaScript에서 데이터 처리
|
||||
```javascript
|
||||
// 안전한 방법
|
||||
var safeData = $('<div>').text(userInput).html();
|
||||
|
||||
// 위험한 방법
|
||||
var dangerousData = userInput; // XSS 공격 가능
|
||||
```
|
||||
|
||||
## CSRF 방지
|
||||
|
||||
### 토큰 기반 CSRF 방지
|
||||
```jsp
|
||||
<!-- 폼에 CSRF 토큰 포함 -->
|
||||
<form method="post" action="save.do">
|
||||
<input type="hidden" name="csrfToken" value="${csrfToken}"/>
|
||||
<!-- 기타 입력 필드 -->
|
||||
</form>
|
||||
```
|
||||
|
||||
```java
|
||||
// 컨트롤러에서 CSRF 토큰 검증
|
||||
@RequestMapping("/save.do")
|
||||
public String save(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
|
||||
String sessionToken = (String)request.getSession().getAttribute("csrfToken");
|
||||
String requestToken = (String)paramMap.get("csrfToken");
|
||||
|
||||
if (!sessionToken.equals(requestToken)) {
|
||||
throw new SecurityException("CSRF 토큰이 일치하지 않습니다.");
|
||||
}
|
||||
|
||||
// 정상 처리
|
||||
}
|
||||
```
|
||||
|
||||
## 로깅 및 감사
|
||||
|
||||
### 보안 이벤트 로깅
|
||||
```java
|
||||
private static final Logger securityLogger = LoggerFactory.getLogger("SECURITY");
|
||||
|
||||
public void logSecurityEvent(String event, String userId, String details) {
|
||||
securityLogger.info("Security Event: {} | User: {} | Details: {}", event, userId, details);
|
||||
}
|
||||
|
||||
// 사용 예시
|
||||
logSecurityEvent("LOGIN_SUCCESS", userId, request.getRemoteAddr());
|
||||
logSecurityEvent("ACCESS_DENIED", userId, request.getRequestURI());
|
||||
```
|
||||
|
||||
### 민감 정보 마스킹
|
||||
```java
|
||||
public String maskSensitiveData(String data) {
|
||||
if (data == null || data.length() < 4) {
|
||||
return "****";
|
||||
}
|
||||
return data.substring(0, 2) + "****" + data.substring(data.length() - 2);
|
||||
}
|
||||
```
|
||||
|
||||
## 환경별 보안 설정
|
||||
|
||||
### 개발 환경
|
||||
- 상세한 오류 메시지 표시
|
||||
- 디버그 모드 활성화
|
||||
- 보안 제약 완화
|
||||
|
||||
### 운영 환경
|
||||
- 일반적인 오류 메시지만 표시
|
||||
- 디버그 모드 비활성화
|
||||
- 엄격한 보안 정책 적용
|
||||
|
||||
```java
|
||||
// 환경별 설정 예시
|
||||
if (Constants.IS_PRODUCTION) {
|
||||
// 운영 환경 설정
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "접근이 거부되었습니다.");
|
||||
} else {
|
||||
// 개발 환경 설정
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "권한 부족: " + detailedMessage);
|
||||
}
|
||||
```
|
||||
|
||||
## 보안 체크리스트
|
||||
|
||||
### 코드 레벨
|
||||
- [ ] SQL 인젝션 방지 (#{} 파라미터 바인딩 사용)
|
||||
- [ ] XSS 방지 (출력값 이스케이프)
|
||||
- [ ] CSRF 방지 (토큰 검증)
|
||||
- [ ] 입력값 검증 및 정제
|
||||
- [ ] 패스워드 암호화 저장
|
||||
|
||||
### 설정 레벨
|
||||
- [ ] 세션 타임아웃 설정
|
||||
- [ ] 파일 업로드 제한
|
||||
- [ ] 오류 페이지 설정
|
||||
- [ ] HTTPS 사용 (운영 환경)
|
||||
- [ ] 보안 헤더 설정
|
||||
|
||||
### 운영 레벨
|
||||
- [ ] 정기적인 보안 점검
|
||||
- [ ] 로그 모니터링
|
||||
- [ ] 권한 정기 검토
|
||||
- [ ] 패스워드 정책 적용
|
||||
- [ ] 백업 데이터 암호화
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# 트러블슈팅 가이드
|
||||
|
||||
## 일반적인 문제 해결
|
||||
|
||||
### 애플리케이션 시작 오류
|
||||
|
||||
#### 데이터베이스 연결 실패
|
||||
```
|
||||
원인: JNDI DataSource 설정 문제
|
||||
해결:
|
||||
1. tomcat-conf/context.xml 확인
|
||||
2. PostgreSQL 서비스 상태 확인
|
||||
3. 데이터베이스 접속 정보 확인
|
||||
```
|
||||
|
||||
#### 클래스 로딩 오류
|
||||
```
|
||||
원인: 컴파일되지 않은 Java 파일
|
||||
해결:
|
||||
1. Eclipse에서 프로젝트 Clean & Build
|
||||
2. WebContent/WEB-INF/classes/ 디렉토리 확인
|
||||
3. 누락된 라이브러리 확인 (WebContent/WEB-INF/lib/)
|
||||
```
|
||||
|
||||
### 런타임 오류
|
||||
|
||||
#### 404 오류 (페이지를 찾을 수 없음)
|
||||
```
|
||||
원인: URL 매핑 문제
|
||||
해결:
|
||||
1. @RequestMapping 어노테이션 확인
|
||||
2. JSP 파일 경로 확인 (/WEB-INF/view/)
|
||||
3. web.xml의 servlet-mapping 확인 (*.do 패턴)
|
||||
```
|
||||
|
||||
#### 500 오류 (서버 내부 오류)
|
||||
```
|
||||
원인: Java 코드 실행 오류
|
||||
해결:
|
||||
1. 로그 파일 확인 (log4j 설정)
|
||||
2. MyBatis SQL 오류 확인
|
||||
3. NullPointerException 체크
|
||||
4. 데이터베이스 트랜잭션 오류 확인
|
||||
```
|
||||
|
||||
### 데이터베이스 관련 문제
|
||||
|
||||
#### SQL 실행 오류
|
||||
```
|
||||
원인: MyBatis 매퍼 설정 문제
|
||||
해결:
|
||||
1. mapper XML 파일의 SQL 문법 확인
|
||||
2. parameterType과 resultType 확인
|
||||
3. 테이블/컬럼명 대소문자 확인 (PostgreSQL)
|
||||
```
|
||||
|
||||
#### 트랜잭션 문제
|
||||
```
|
||||
원인: SqlSession 관리 문제
|
||||
해결:
|
||||
1. SqlSession.close() 호출 확인
|
||||
2. try-finally 블록에서 리소스 정리
|
||||
3. 트랜잭션 커밋/롤백 처리
|
||||
```
|
||||
|
||||
### 프론트엔드 문제
|
||||
|
||||
#### JavaScript 오류
|
||||
```
|
||||
원인: jQuery/jqGrid 라이브러리 문제
|
||||
해결:
|
||||
1. 브라우저 개발자 도구 콘솔 확인
|
||||
2. JavaScript 파일 로딩 순서 확인
|
||||
3. jQuery 버전 호환성 확인
|
||||
```
|
||||
|
||||
#### CSS 스타일 문제
|
||||
```
|
||||
원인: CSS 파일 로딩 또는 경로 문제
|
||||
해결:
|
||||
1. CSS 파일 경로 확인
|
||||
2. 브라우저 캐시 클리어
|
||||
3. all.css 파일 확인
|
||||
```
|
||||
|
||||
## 개발 환경 문제
|
||||
|
||||
### Docker 환경
|
||||
```bash
|
||||
# 컨테이너 로그 확인
|
||||
docker-compose logs app
|
||||
|
||||
# 컨테이너 내부 접속
|
||||
docker-compose exec app bash
|
||||
|
||||
# 데이터베이스 접속 확인
|
||||
docker-compose exec db psql -U postgres -d ilshin
|
||||
```
|
||||
|
||||
### Eclipse 환경
|
||||
```
|
||||
문제: 프로젝트 인식 오류
|
||||
해결:
|
||||
1. .project 파일 확인
|
||||
2. .classpath 파일 확인
|
||||
3. Project Properties > Java Build Path 확인
|
||||
4. Server Runtime 설정 확인 (Tomcat 7.0)
|
||||
```
|
||||
|
||||
## 로그 분석
|
||||
|
||||
### 주요 로그 위치
|
||||
- 애플리케이션 로그: log4j 설정에 따름
|
||||
- Tomcat 로그: `$CATALINA_HOME/logs/`
|
||||
- Docker 로그: `docker-compose logs`
|
||||
|
||||
### 로그 설정 파일
|
||||
- [log4j.xml](mdc:WebContent/WEB-INF/log4j.xml) - 로깅 설정
|
||||
- 로그 레벨 조정으로 상세 정보 확인 가능
|
||||
|
||||
## 성능 문제
|
||||
|
||||
### 데이터베이스 성능
|
||||
```sql
|
||||
-- 슬로우 쿼리 확인
|
||||
SELECT query, mean_time, calls
|
||||
FROM pg_stat_statements
|
||||
ORDER BY mean_time DESC;
|
||||
|
||||
-- 인덱스 사용률 확인
|
||||
SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetch
|
||||
FROM pg_stat_user_indexes;
|
||||
```
|
||||
|
||||
### 메모리 문제
|
||||
```
|
||||
원인: 메모리 누수 또는 부족
|
||||
해결:
|
||||
1. JVM 힙 메모리 설정 확인
|
||||
2. SqlSession 리소스 정리 확인
|
||||
3. 대량 데이터 처리 시 페이징 적용
|
||||
```
|
||||
|
||||
## 보안 관련
|
||||
|
||||
### 권한 문제
|
||||
```
|
||||
원인: 사용자 권한 설정 오류
|
||||
해결:
|
||||
1. MENU_AUTH_GROUP 테이블 확인
|
||||
2. 사용자 세션 정보 확인
|
||||
3. Spring Security 설정 확인 (있는 경우)
|
||||
```
|
||||
|
||||
### SQL 인젝션 방지
|
||||
```
|
||||
주의사항:
|
||||
1. MyBatis #{} 파라미터 바인딩 사용
|
||||
2. ${} 직접 문자열 치환 지양
|
||||
3. 사용자 입력값 검증
|
||||
```
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# PLM ILSHIN 개발환경 설정
|
||||
|
||||
# 애플리케이션 환경
|
||||
NODE_ENV=development
|
||||
|
||||
# 데이터베이스 설정
|
||||
DB_URL=jdbc:postgresql://39.117.244.52:11132/plm
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=ph0909!!
|
||||
|
||||
# PostgreSQL 환경 변수 (내부 DB 사용 시)
|
||||
POSTGRES_DB=plm
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=ph0909!!
|
||||
|
||||
# 애플리케이션 포트
|
||||
APP_PORT=8090
|
||||
|
||||
# JVM 옵션
|
||||
JAVA_OPTS="-Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m"
|
||||
|
||||
# 로그 레벨
|
||||
LOG_LEVEL=DEBUG
|
||||
|
||||
# 개발 모드 플래그
|
||||
DEBUG=true
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
# Dependencies
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
.env.local
|
||||
.env.production
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
public
|
||||
|
||||
# Storybook build outputs
|
||||
.out
|
||||
.storybook-out
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Prisma
|
||||
prisma/migrations/
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Test coverage
|
||||
coverage/
|
||||
|
||||
# ===== 민감한 정보 보호 =====
|
||||
|
||||
# 데이터베이스 연결 정보
|
||||
backend-node/prisma/schema.prisma
|
||||
backend-node/.env
|
||||
backend-node/.env.local
|
||||
backend-node/.env.development
|
||||
backend-node/.env.production
|
||||
backend-node/.env.test
|
||||
|
||||
# 백엔드 환경 변수
|
||||
backend/.env
|
||||
backend/.env.local
|
||||
backend/.env.development
|
||||
backend/.env.production
|
||||
backend/.env.test
|
||||
|
||||
# 프론트엔드 환경 변수
|
||||
frontend/.env
|
||||
frontend/.env.local
|
||||
frontend/.env.development
|
||||
frontend/.env.production
|
||||
frontend/.env.test
|
||||
|
||||
# Docker 관련 민감 정보
|
||||
docker-compose.override.yml
|
||||
docker-compose.prod.yml
|
||||
.env.docker
|
||||
|
||||
# 설정 파일들
|
||||
config/
|
||||
configs/
|
||||
settings/
|
||||
*.config.js
|
||||
*.config.ts
|
||||
*.config.json
|
||||
|
||||
# 로그 파일들
|
||||
*.log
|
||||
logs/
|
||||
log/
|
||||
|
||||
# 임시 파일들
|
||||
*.tmp
|
||||
*.temp
|
||||
temp/
|
||||
tmp/
|
||||
|
||||
# 백업 파일들
|
||||
*.bak
|
||||
*.backup
|
||||
*.old
|
||||
backup/
|
||||
|
||||
# 키 파일들
|
||||
*.key
|
||||
*.pem
|
||||
*.p12
|
||||
*.pfx
|
||||
*.crt
|
||||
*.cert
|
||||
|
||||
# API 키 및 토큰
|
||||
secrets/
|
||||
secrets.json
|
||||
secrets.yaml
|
||||
secrets.yml
|
||||
api-keys.json
|
||||
tokens.json
|
||||
|
||||
# 데이터베이스 덤프 파일
|
||||
*.sql
|
||||
*.dump
|
||||
*.backup
|
||||
db/dump/
|
||||
db/backup/
|
||||
|
||||
# 캐시 파일들
|
||||
.cache/
|
||||
cache/
|
||||
*.cache
|
||||
|
||||
# 사용자별 설정
|
||||
.vscode/settings.json
|
||||
.idea/workspace.xml
|
||||
*.user
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>ilshin</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
||||
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
|
||||
</natures>
|
||||
<filteredResources>
|
||||
<filter>
|
||||
<id>1746619144814</id>
|
||||
<name></name>
|
||||
<type>30</type>
|
||||
<matcher>
|
||||
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||
</matcher>
|
||||
</filter>
|
||||
</filteredResources>
|
||||
</projectDescription>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry excluding="**/*.min.js|**/node_modules/*|**/bower_components/*" kind="src" path="WebContent"/>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
|
||||
<attributes>
|
||||
<attribute name="hide" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
|
||||
<classpathentry kind="output" path=""/>
|
||||
</classpath>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding//WebContent/WEB-INF/view/materMgmt/materOrderDown1.jsp=UTF-8
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
|
||||
<wb-module deploy-name="plm">
|
||||
<wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
|
||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
|
||||
<property name="context-root" value="plm"/>
|
||||
<property name="java-output-path" value="/plm/build/classes"/>
|
||||
</wb-module>
|
||||
</project-modules>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<faceted-project>
|
||||
<runtime name="ilshin"/>
|
||||
<fixed facet="wst.jsdt.web"/>
|
||||
<fixed facet="jst.web"/>
|
||||
<fixed facet="java"/>
|
||||
<installed facet="java" version="1.7"/>
|
||||
<installed facet="jst.web" version="3.0"/>
|
||||
<installed facet="wst.jsdt.web" version="1.0"/>
|
||||
</faceted-project>
|
||||
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.wst.jsdt.launching.baseBrowserLibrary
|
||||
|
|
@ -0,0 +1 @@
|
|||
Window
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
DELEGATES_PREFERENCE=delegateValidatorList
|
||||
USER_BUILD_PREFERENCE=enabledBuildValidatorListorg.eclipse.jst.j2ee.internal.classpathdep.ClasspathDependencyValidator;
|
||||
USER_MANUAL_PREFERENCE=enabledManualValidatorListorg.eclipse.jst.j2ee.internal.classpathdep.ClasspathDependencyValidator;
|
||||
USER_PREFERENCE=overrideGlobalPreferencestruedisableAllValidationtrueversion1.2.700.v201508251749
|
||||
eclipse.preferences.version=1
|
||||
override=true
|
||||
suspend=true
|
||||
vf.version=3
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
# PLM WACE Docker 설정 가이드
|
||||
|
||||
## 개요
|
||||
이 문서는 PLM WACE 애플리케이션을 Docker로 실행하는 방법을 설명합니다.
|
||||
|
||||
## 시스템 요구사항
|
||||
|
||||
### 리눅스 환경
|
||||
- Ubuntu 18.04 이상 또는 CentOS 7 이상
|
||||
- Docker 20.10 이상
|
||||
- Docker Compose 1.29 이상
|
||||
- Git (운영환경 배포 시)
|
||||
|
||||
### 필수 소프트웨어 설치
|
||||
|
||||
#### Docker 설치 (Ubuntu)
|
||||
```bash
|
||||
# Docker 공식 GPG 키 추가
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
|
||||
# Docker 리포지토리 추가
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
# Docker 설치
|
||||
sudo apt update
|
||||
sudo apt install docker-ce docker-ce-cli containerd.io
|
||||
|
||||
# Docker Compose 설치
|
||||
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
# 사용자를 docker 그룹에 추가
|
||||
sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
#### Docker 설치 (CentOS)
|
||||
```bash
|
||||
# Docker 설치
|
||||
sudo yum install -y yum-utils
|
||||
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
||||
sudo yum install docker-ce docker-ce-cli containerd.io
|
||||
|
||||
# Docker Compose 설치
|
||||
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
# Docker 서비스 시작
|
||||
sudo systemctl start docker
|
||||
sudo systemctl enable docker
|
||||
|
||||
# 사용자를 docker 그룹에 추가
|
||||
sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
## 환경 설정
|
||||
|
||||
### 1. 환경 변수 파일 생성
|
||||
|
||||
#### 개발환경
|
||||
```bash
|
||||
# 개발환경 환경 변수 파일 생성
|
||||
cp env.development.example .env.development
|
||||
|
||||
# 필요에 따라 설정 수정
|
||||
vim .env.development
|
||||
```
|
||||
|
||||
#### 운영환경
|
||||
```bash
|
||||
# 운영환경 환경 변수 파일 생성
|
||||
cp env.production.example .env.production
|
||||
|
||||
# 운영환경에 맞게 설정 수정 (특히 비밀번호)
|
||||
vim .env.production
|
||||
```
|
||||
|
||||
### 2. 환경 변수 설정 항목
|
||||
|
||||
#### 주요 설정 항목
|
||||
- `DB_URL`: 데이터베이스 연결 URL
|
||||
- `DB_USERNAME`: 데이터베이스 사용자명
|
||||
- `DB_PASSWORD`: 데이터베이스 비밀번호
|
||||
- `JAVA_OPTS`: JVM 옵션 (메모리 설정 등)
|
||||
- `LOG_LEVEL`: 로그 레벨 (DEBUG, INFO, WARN, ERROR)
|
||||
|
||||
## 스크립트 사용법
|
||||
|
||||
### 기본 사용법
|
||||
```bash
|
||||
# 실행 권한 부여 (최초 1회)
|
||||
chmod +x start-docker-linux.sh
|
||||
|
||||
# 개발환경 실행
|
||||
./start-docker-linux.sh
|
||||
|
||||
# 운영환경 실행
|
||||
./start-docker-linux.sh -e prod
|
||||
```
|
||||
|
||||
### 주요 옵션
|
||||
|
||||
#### 환경 설정
|
||||
```bash
|
||||
# 개발환경 실행
|
||||
./start-docker-linux.sh -e dev
|
||||
|
||||
# 운영환경 실행
|
||||
./start-docker-linux.sh -e prod
|
||||
```
|
||||
|
||||
#### 컨테이너 관리
|
||||
```bash
|
||||
# 컨테이너 중지
|
||||
./start-docker-linux.sh -s
|
||||
|
||||
# 컨테이너 재시작
|
||||
./start-docker-linux.sh -r
|
||||
|
||||
# Docker 시스템 정리 후 실행
|
||||
./start-docker-linux.sh -c
|
||||
```
|
||||
|
||||
#### 로그 및 모니터링
|
||||
```bash
|
||||
# 실시간 로그 확인
|
||||
./start-docker-linux.sh -l
|
||||
|
||||
# 도움말 확인
|
||||
./start-docker-linux.sh -h
|
||||
```
|
||||
|
||||
### 옵션 조합
|
||||
```bash
|
||||
# 개발환경에서 Docker 정리 후 재시작
|
||||
./start-docker-linux.sh -e dev -c -r
|
||||
|
||||
# 운영환경에서 재시작
|
||||
./start-docker-linux.sh -e prod -r
|
||||
```
|
||||
|
||||
## 접속 정보
|
||||
|
||||
### 개발환경
|
||||
- 애플리케이션: http://localhost:8090
|
||||
- 데이터베이스: localhost:5432 (내부 DB 사용 시)
|
||||
|
||||
### 운영환경
|
||||
- 애플리케이션: https://ilshin.esgrin.com
|
||||
- 대체 도메인: https://autoclave.co.kr
|
||||
|
||||
## 트러블슈팅
|
||||
|
||||
### 일반적인 문제
|
||||
|
||||
#### 1. Docker 서비스 오류
|
||||
```bash
|
||||
# Docker 서비스 상태 확인
|
||||
sudo systemctl status docker
|
||||
|
||||
# Docker 서비스 시작
|
||||
sudo systemctl start docker
|
||||
|
||||
# Docker 서비스 자동 시작 설정
|
||||
sudo systemctl enable docker
|
||||
```
|
||||
|
||||
#### 2. 권한 오류
|
||||
```bash
|
||||
# 사용자를 docker 그룹에 추가
|
||||
sudo usermod -aG docker $USER
|
||||
|
||||
# 로그아웃 후 재로그인 또는 그룹 변경 적용
|
||||
newgrp docker
|
||||
```
|
||||
|
||||
#### 3. 포트 충돌
|
||||
```bash
|
||||
# 포트 사용 확인
|
||||
sudo netstat -tlnp | grep :8090
|
||||
|
||||
# 프로세스 종료
|
||||
sudo kill -9 <PID>
|
||||
```
|
||||
|
||||
#### 4. 환경 변수 파일 오류
|
||||
```bash
|
||||
# 환경 변수 파일 존재 확인
|
||||
ls -la .env.*
|
||||
|
||||
# 환경 변수 파일 내용 확인
|
||||
cat .env.development
|
||||
```
|
||||
|
||||
### 로그 확인
|
||||
|
||||
#### 컨테이너 로그
|
||||
```bash
|
||||
# 전체 로그 확인
|
||||
./start-docker-linux.sh -l
|
||||
|
||||
# 특정 서비스 로그 확인
|
||||
docker-compose -f docker-compose.dev.yml logs plm-ilshin
|
||||
|
||||
# 로그 파일 확인 (컨테이너 내부)
|
||||
docker exec -it plm-ilshin-container tail -f /usr/local/tomcat/logs/catalina.out
|
||||
```
|
||||
|
||||
#### 시스템 로그
|
||||
```bash
|
||||
# Docker 데몬 로그 확인
|
||||
sudo journalctl -u docker.service
|
||||
|
||||
# 시스템 로그 확인
|
||||
sudo journalctl -xe
|
||||
```
|
||||
|
||||
## 고급 사용법
|
||||
|
||||
### 수동 Docker Compose 사용
|
||||
```bash
|
||||
# 개발환경 수동 실행
|
||||
docker-compose -f docker-compose.dev.yml up -d
|
||||
|
||||
# 운영환경 수동 실행
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# 컨테이너 중지
|
||||
docker-compose -f docker-compose.dev.yml down
|
||||
```
|
||||
|
||||
### 컨테이너 내부 접근
|
||||
```bash
|
||||
# 컨테이너 내부 접근
|
||||
docker exec -it plm-ilshin-container bash
|
||||
|
||||
# 데이터베이스 접근 (내부 DB 사용 시)
|
||||
docker exec -it plm-ilshin-db-container psql -U postgres -d ilshin
|
||||
```
|
||||
|
||||
### 백업 및 복원
|
||||
```bash
|
||||
# 데이터베이스 백업
|
||||
docker exec plm-ilshin-db-container pg_dump -U postgres ilshin > backup.sql
|
||||
|
||||
# 데이터베이스 복원
|
||||
docker exec -i plm-ilshin-db-container psql -U postgres ilshin < backup.sql
|
||||
```
|
||||
|
||||
## 보안 고려사항
|
||||
|
||||
### 운영환경 보안
|
||||
1. 환경 변수 파일 권한 설정
|
||||
```bash
|
||||
chmod 600 .env.production
|
||||
```
|
||||
|
||||
2. 방화벽 설정
|
||||
```bash
|
||||
# 필요한 포트만 열기
|
||||
sudo ufw allow 80/tcp
|
||||
sudo ufw allow 443/tcp
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
3. SSL 인증서 설정 (Traefik 사용)
|
||||
- Let's Encrypt 자동 갱신 설정
|
||||
- 도메인 검증 설정
|
||||
|
||||
### 개발환경 보안
|
||||
1. 개발용 비밀번호 사용
|
||||
2. 외부 접근 제한
|
||||
3. 정기적인 이미지 업데이트
|
||||
|
||||
## 성능 최적화
|
||||
|
||||
### JVM 튜닝
|
||||
```bash
|
||||
# .env 파일에서 JVM 옵션 조정
|
||||
JAVA_OPTS=-Xms1024m -Xmx2048m -XX:PermSize=512m -XX:MaxPermSize=1024m
|
||||
```
|
||||
|
||||
### Docker 리소스 제한
|
||||
```yaml
|
||||
# docker-compose.yml에서 리소스 제한
|
||||
services:
|
||||
plm-ilshin:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2.0'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '1.0'
|
||||
memory: 1G
|
||||
```
|
||||
|
||||
## 모니터링
|
||||
|
||||
### 컨테이너 상태 모니터링
|
||||
```bash
|
||||
# 컨테이너 상태 확인
|
||||
docker ps
|
||||
|
||||
# 리소스 사용량 확인
|
||||
docker stats
|
||||
|
||||
# 컨테이너 로그 모니터링
|
||||
docker logs -f plm-ilshin-container
|
||||
```
|
||||
|
||||
### 애플리케이션 모니터링
|
||||
- 애플리케이션 로그 확인
|
||||
- 데이터베이스 연결 상태 확인
|
||||
- 메모리 사용량 모니터링
|
||||
|
||||
## 지원 및 문의
|
||||
|
||||
문제가 발생하거나 추가 도움이 필요한 경우:
|
||||
1. 로그 파일 확인
|
||||
2. 환경 설정 검토
|
||||
3. 개발팀 문의
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
FROM localhost:8787/tomcat:7.0.94-jre7-alpine.linux AS production
|
||||
|
||||
# Remove default webapps
|
||||
RUN rm -rf /usr/local/tomcat/webapps/*
|
||||
|
||||
# Copy web application content (compiled classes and web resources)
|
||||
COPY WebContent /usr/local/tomcat/webapps/ROOT
|
||||
COPY src /usr/local/tomcat/webapps/ROOT/WEB-INF/src
|
||||
|
||||
# Copy custom Tomcat context configuration for JNDI
|
||||
COPY ./tomcat-conf/context.xml /usr/local/tomcat/conf/context.xml
|
||||
|
||||
# Copy database driver if needed (PostgreSQL driver is already in WEB-INF/lib)
|
||||
# COPY path/to/postgresql-driver.jar /usr/local/tomcat/lib/
|
||||
|
||||
# Expose Tomcat port
|
||||
EXPOSE 8080
|
||||
|
||||
# Start Tomcat
|
||||
CMD ["catalina.sh", "run"]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
FROM localhost:8787/tomcat:7.0.94-jre7-alpine.linux AS Development
|
||||
|
||||
# Remove default webapps
|
||||
RUN rm -rf /usr/local/tomcat/webapps/*
|
||||
|
||||
# Copy web application content (compiled classes and web resources)
|
||||
COPY WebContent /usr/local/tomcat/webapps/ROOT
|
||||
COPY src /usr/local/tomcat/webapps/ROOT/WEB-INF/src
|
||||
|
||||
# Copy custom Tomcat context configuration for JNDI
|
||||
COPY ./tomcat-conf/context.xml /usr/local/tomcat/conf/context.xml
|
||||
|
||||
# Copy database driver if needed (PostgreSQL driver is already in WEB-INF/lib)
|
||||
# COPY path/to/postgresql-driver.jar /usr/local/tomcat/lib/
|
||||
|
||||
# Expose Tomcat port
|
||||
EXPOSE 8080
|
||||
|
||||
# Start Tomcat
|
||||
CMD ["catalina.sh", "run"]
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# 윈도우용 PLM 애플리케이션 Dockerfile
|
||||
FROM tomcat:7.0.94-jre7-alpine
|
||||
|
||||
# 메타데이터
|
||||
LABEL maintainer="PLM Development Team"
|
||||
LABEL description="PLM Application for Windows Environment"
|
||||
LABEL version="1.0"
|
||||
|
||||
# 작업 디렉토리 설정
|
||||
WORKDIR /usr/local/tomcat
|
||||
|
||||
# 필수 패키지 설치 (curl 포함)
|
||||
RUN apk add --no-cache curl tzdata
|
||||
|
||||
# 환경 변수 설정
|
||||
ENV CATALINA_HOME=/usr/local/tomcat
|
||||
ENV CATALINA_BASE=/usr/local/tomcat
|
||||
ENV JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m"
|
||||
ENV TZ=Asia/Seoul
|
||||
|
||||
# 타임존 설정 (서울)
|
||||
RUN cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
|
||||
echo "Asia/Seoul" > /etc/timezone
|
||||
|
||||
# 기본 Tomcat 애플리케이션 제거
|
||||
RUN rm -rf /usr/local/tomcat/webapps/*
|
||||
|
||||
# 애플리케이션 복사
|
||||
COPY WebContent/ /usr/local/tomcat/webapps/ROOT/
|
||||
COPY tomcat-conf/context.xml /usr/local/tomcat/conf/
|
||||
|
||||
# 로그 디렉토리 생성
|
||||
RUN mkdir -p /usr/local/tomcat/logs
|
||||
|
||||
# 권한 설정
|
||||
RUN chmod -R 755 /usr/local/tomcat/webapps/ROOT && \
|
||||
chmod 644 /usr/local/tomcat/conf/context.xml
|
||||
|
||||
# 포트 노출
|
||||
EXPOSE 8080
|
||||
|
||||
# 개선된 헬스체크 (윈도우 환경 고려)
|
||||
HEALTHCHECK --interval=30s --timeout=15s --start-period=90s --retries=5 \
|
||||
CMD curl -f http://localhost:8080/ROOT/ || curl -f http://localhost:8080/ || exit 1
|
||||
|
||||
# 실행 명령
|
||||
CMD ["catalina.sh", "run"]
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
# PLM 윈도우 개발환경 가이드
|
||||
|
||||
## 🚀 빠른 시작
|
||||
|
||||
### 1. 전체 환경 시작
|
||||
```cmd
|
||||
start-windows.bat
|
||||
```
|
||||
|
||||
### 2. 환경 중지
|
||||
```cmd
|
||||
stop-windows.bat
|
||||
```
|
||||
|
||||
### 3. 빌드만 실행
|
||||
```cmd
|
||||
build-windows.bat
|
||||
```
|
||||
|
||||
## 📋 사전 요구사항
|
||||
|
||||
### 필수 소프트웨어
|
||||
- **Docker Desktop for Windows** (WSL2 백엔드 사용)
|
||||
- **Java Development Kit (JDK) 7 이상**
|
||||
- **Git for Windows**
|
||||
|
||||
### Docker Desktop 설정
|
||||
1. Docker Desktop 설치
|
||||
2. **Settings > General**에서 "Use WSL 2 based engine" 체크
|
||||
3. **Settings > Resources > WSL Integration**에서 WSL 배포판 활성화
|
||||
|
||||
## 📁 파일 구조
|
||||
|
||||
```
|
||||
new_ph/
|
||||
├── start-windows.bat # 🎯 메인 시작 스크립트
|
||||
├── stop-windows.bat # ⏹️ 중지 스크립트
|
||||
├── build-windows.bat # 🔨 Java 빌드 스크립트
|
||||
├── docker-compose.win.yml # 🐳 윈도우용 Docker Compose
|
||||
├── Dockerfile.win # 🐳 윈도우용 Dockerfile
|
||||
├── config.windows.env # ⚙️ 환경 변수 설정
|
||||
└── README-WINDOWS.md # 📖 이 파일
|
||||
```
|
||||
|
||||
## ⚙️ 환경 설정
|
||||
|
||||
### config.windows.env 파일 수정
|
||||
```env
|
||||
# 데이터베이스 설정
|
||||
DB_PASSWORD=your_password_here
|
||||
|
||||
# 포트 설정 (충돌 시 변경)
|
||||
TOMCAT_PORT=9090
|
||||
|
||||
# 메모리 설정
|
||||
TOMCAT_MEMORY_MIN=512m
|
||||
TOMCAT_MEMORY_MAX=1024m
|
||||
```
|
||||
|
||||
## 🐳 Docker 서비스
|
||||
|
||||
### 애플리케이션 서비스
|
||||
- **컨테이너명**: plm-windows
|
||||
- **포트**: 9090 → 8080
|
||||
- **접속 URL**: http://localhost:9090
|
||||
|
||||
### 데이터베이스 서비스
|
||||
- **컨테이너명**: plm-postgres-win
|
||||
- **포트**: 5432 → 5432
|
||||
- **데이터베이스**: plm
|
||||
- **사용자**: postgres
|
||||
- **패스워드**: ph0909!!
|
||||
|
||||
## 🔧 주요 명령어
|
||||
|
||||
### Docker 관리
|
||||
```cmd
|
||||
# 컨테이너 상태 확인
|
||||
docker-compose -f docker-compose.win.yml ps
|
||||
|
||||
# 로그 확인
|
||||
docker-compose -f docker-compose.win.yml logs -f
|
||||
|
||||
# 특정 서비스 로그
|
||||
docker-compose -f docker-compose.win.yml logs -f plm-app
|
||||
docker-compose -f docker-compose.win.yml logs -f plm-db
|
||||
|
||||
# 컨테이너 재시작
|
||||
docker-compose -f docker-compose.win.yml restart plm-app
|
||||
```
|
||||
|
||||
### 개발 작업
|
||||
```cmd
|
||||
# 빌드만 실행
|
||||
build-windows.bat
|
||||
|
||||
# 컨테이너 재빌드
|
||||
docker-compose -f docker-compose.win.yml up --build -d
|
||||
|
||||
# 데이터베이스 리셋
|
||||
docker-compose -f docker-compose.win.yml down -v
|
||||
docker-compose -f docker-compose.win.yml up -d
|
||||
```
|
||||
|
||||
## 🐛 문제 해결
|
||||
|
||||
### Docker Desktop 실행 안됨
|
||||
```cmd
|
||||
# Windows 서비스 확인
|
||||
sc query com.docker.service
|
||||
|
||||
# WSL2 상태 확인
|
||||
wsl --status
|
||||
|
||||
# Docker Desktop 재시작
|
||||
taskkill /f /im "Docker Desktop.exe"
|
||||
start "" "C:\Program Files\Docker\Docker\Docker Desktop.exe"
|
||||
```
|
||||
|
||||
### Java 컴파일 오류
|
||||
```cmd
|
||||
# Java 버전 확인
|
||||
java -version
|
||||
javac -version
|
||||
|
||||
# 클래스패스 문제 시 수동 빌드
|
||||
javac -cp "WebContent\WEB-INF\lib\*" -d WebContent\WEB-INF\classes src\com\pms\**\*.java
|
||||
```
|
||||
|
||||
### 포트 충돌
|
||||
```cmd
|
||||
# 포트 사용 확인
|
||||
netstat -ano | findstr :9090
|
||||
|
||||
# 프로세스 종료
|
||||
taskkill /PID <PID번호> /F
|
||||
```
|
||||
|
||||
### 볼륨 권한 문제
|
||||
```cmd
|
||||
# Docker 볼륨 정리
|
||||
docker volume prune -f
|
||||
|
||||
# WSL2 재시작
|
||||
wsl --shutdown
|
||||
```
|
||||
|
||||
## 📊 모니터링
|
||||
|
||||
### 리소스 사용량
|
||||
```cmd
|
||||
# Docker 시스템 정보
|
||||
docker system df
|
||||
|
||||
# 컨테이너 리소스 사용량
|
||||
docker stats
|
||||
|
||||
# 로그 크기 확인
|
||||
dir logs /s
|
||||
```
|
||||
|
||||
### 헬스체크
|
||||
```cmd
|
||||
# 애플리케이션 상태
|
||||
curl http://localhost:9090
|
||||
|
||||
# 데이터베이스 연결 테스트
|
||||
docker exec plm-postgres-win psql -U postgres -d plm -c "SELECT version();"
|
||||
```
|
||||
|
||||
## 🔄 업데이트
|
||||
|
||||
### 코드 변경 후
|
||||
1. `build-windows.bat` 실행
|
||||
2. `docker-compose -f docker-compose.win.yml restart plm-app`
|
||||
|
||||
### Docker 이미지 업데이트
|
||||
```cmd
|
||||
docker-compose -f docker-compose.win.yml down
|
||||
docker-compose -f docker-compose.win.yml pull
|
||||
docker-compose -f docker-compose.win.yml up --build -d
|
||||
```
|
||||
|
||||
## 📞 지원
|
||||
|
||||
문제가 발생하면 다음을 확인하세요:
|
||||
|
||||
1. **로그 파일**: `logs/` 디렉토리
|
||||
2. **Docker 로그**: `docker-compose -f docker-compose.win.yml logs`
|
||||
3. **시스템 요구사항**: Docker Desktop, WSL2, Java JDK
|
||||
4. **네트워크**: 방화벽, 포트 충돌
|
||||
|
||||
---
|
||||
|
||||
**🎉 즐거운 개발 되세요!**
|
||||
|
|
@ -0,0 +1,472 @@
|
|||
# PLM 솔루션 (WACE)
|
||||
|
||||
## 프로젝트 개요
|
||||
|
||||
본 프로젝트는 제품 수명 주기 관리(PLM - Product Lifecycle Management) 솔루션입니다.
|
||||
**기존 JSP 기반 프론트엔드를 Next.js 14로 완전 전환**하여 현대적이고 사용자 친화적인 웹 애플리케이션을 제공합니다.
|
||||
|
||||
## 🚀 주요 특징
|
||||
|
||||
- **모던 프론트엔드**: Next.js 14 + TypeScript + shadcn/ui
|
||||
- **반응형 디자인**: 데스크톱, 태블릿, 모바일 모든 기기 지원
|
||||
- **안정적인 백엔드**: 검증된 Java Spring + MyBatis 기반 API
|
||||
- **Docker 기반 배포**: 개발/운영 환경 일관성 보장
|
||||
- **타입 안전성**: TypeScript로 런타임 에러 방지
|
||||
|
||||
## 🛠️ 기술 스택
|
||||
|
||||
### Frontend (Next.js 14)
|
||||
|
||||
- **프레임워크**: Next.js 14 (App Router)
|
||||
- **언어**: TypeScript
|
||||
- **UI 라이브러리**: shadcn/ui + Radix UI
|
||||
- **스타일링**: Tailwind CSS
|
||||
- **폼 처리**: React Hook Form + Zod
|
||||
- **상태 관리**: React Context + Hooks
|
||||
- **아이콘**: Lucide React
|
||||
|
||||
### Backend (기존 유지)
|
||||
|
||||
- **언어**: Java 7
|
||||
- **프레임워크**: Spring Framework 3.2.4
|
||||
- **ORM**: MyBatis 3.2.3
|
||||
- **데이터베이스**: PostgreSQL
|
||||
- **WAS**: Apache Tomcat 7.0
|
||||
|
||||
### 개발 도구
|
||||
|
||||
- **컨테이너화**: Docker + Docker Compose
|
||||
- **코드 품질**: ESLint + TypeScript
|
||||
- **패키지 관리**: npm
|
||||
|
||||
## 📁 프로젝트 구조
|
||||
|
||||
```
|
||||
new_ph/
|
||||
├── frontend/ # Next.js 프론트엔드
|
||||
│ ├── app/ # Next.js App Router
|
||||
│ │ ├── (auth)/ # 인증 관련 페이지
|
||||
│ │ │ └── login/ # 로그인 페이지
|
||||
│ │ ├── dashboard/ # 대시보드
|
||||
│ │ └── layout.tsx # 루트 레이아웃
|
||||
│ ├── components/ # 재사용 컴포넌트
|
||||
│ │ ├── ui/ # shadcn/ui 기본 컴포넌트
|
||||
│ │ └── layout/ # 레이아웃 컴포넌트
|
||||
│ ├── lib/ # 유틸리티 함수
|
||||
│ └── types/ # TypeScript 타입 정의
|
||||
├── src/ # Java 백엔드 소스
|
||||
│ └── com/pms/ # 패키지 구조
|
||||
├── WebContent/ # 레거시 JSP (사용 중단)
|
||||
├── db/ # 데이터베이스 스크립트
|
||||
└── docker-compose.win.yml # Windows 환경 설정
|
||||
```
|
||||
|
||||
## 🚀 빠른 시작
|
||||
|
||||
### 1. 필수 요구사항
|
||||
|
||||
- **Docker Desktop** (Windows/Mac) 또는 **Docker + Docker Compose** (Linux)
|
||||
- **Git** (소스 코드 관리)
|
||||
|
||||
### 2. Windows 환경에서 실행
|
||||
|
||||
#### 자동 실행 (권장)
|
||||
|
||||
```bash
|
||||
# 프로젝트 시작
|
||||
./run-windows.bat
|
||||
|
||||
# 서비스 상태 확인
|
||||
./status-windows.bat
|
||||
|
||||
# 서비스 중지
|
||||
./stop-windows.bat
|
||||
```
|
||||
|
||||
#### 수동 실행
|
||||
|
||||
```bash
|
||||
# Docker 컨테이너 실행
|
||||
docker-compose -f docker-compose.win.yml up --build -d
|
||||
|
||||
# 로그 확인
|
||||
docker-compose -f docker-compose.win.yml logs -f
|
||||
```
|
||||
|
||||
### 3. 서비스 접속
|
||||
|
||||
| 서비스 | URL | 설명 |
|
||||
| -------------- | --------------------- | ------------------------------ |
|
||||
| **프론트엔드** | http://localhost:9771 | Next.js 기반 사용자 인터페이스 |
|
||||
| **백엔드 API** | http://localhost:9090 | Spring 기반 REST API |
|
||||
|
||||
> **주의**: 기존 JSP 화면(`localhost:9090`)은 더 이상 사용하지 않습니다.
|
||||
> 모든 사용자는 **Next.js 프론트엔드(`localhost:9771`)**를 사용해주세요.
|
||||
|
||||
## 🎨 UI/UX 디자인 시스템
|
||||
|
||||
### shadcn/ui 컴포넌트 라이브러리
|
||||
|
||||
- **일관된 디자인**: 전체 애플리케이션에서 통일된 UI 컴포넌트
|
||||
- **접근성**: WCAG 가이드라인 준수
|
||||
- **커스터마이징**: Tailwind CSS로 쉬운 스타일 변경
|
||||
- **다크모드**: 자동 테마 전환 지원
|
||||
|
||||
### 공통 스타일 가이드
|
||||
|
||||
```typescript
|
||||
// 색상 팔레트
|
||||
const colors = {
|
||||
primary: "hsl(222.2 47.4% 11.2%)", // 네이비 블루
|
||||
secondary: "hsl(210 40% 96%)", // 연한 그레이
|
||||
accent: "hsl(210 40% 98%)", // 거의 화이트
|
||||
destructive: "hsl(0 62.8% 30.6%)", // 레드
|
||||
muted: "hsl(210 40% 96%)", // 음소거된 그레이
|
||||
};
|
||||
|
||||
// 타이포그래피
|
||||
const typography = {
|
||||
fontFamily: "Inter, system-ui, sans-serif",
|
||||
fontSize: {
|
||||
xs: "0.75rem", // 12px
|
||||
sm: "0.875rem", // 14px
|
||||
base: "1rem", // 16px
|
||||
lg: "1.125rem", // 18px
|
||||
xl: "1.25rem", // 20px
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 🔧 개발 가이드
|
||||
|
||||
### 컴포넌트 개발 원칙
|
||||
|
||||
#### 1. 재사용 가능한 컴포넌트
|
||||
|
||||
```typescript
|
||||
// components/ui/button.tsx
|
||||
interface ButtonProps {
|
||||
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost";
|
||||
size?: "default" | "sm" | "lg" | "icon";
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Button({
|
||||
variant = "default",
|
||||
size = "default",
|
||||
children,
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
return (
|
||||
<button className={cn(buttonVariants({ variant, size }))} {...props}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 폼 컴포넌트
|
||||
|
||||
```typescript
|
||||
// React Hook Form + Zod 사용
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import * as z from "zod";
|
||||
|
||||
const loginSchema = z.object({
|
||||
userId: z.string().min(1, "사용자 ID를 입력해주세요"),
|
||||
password: z.string().min(1, "비밀번호를 입력해주세요"),
|
||||
});
|
||||
|
||||
export function LoginForm() {
|
||||
const form = useForm<z.infer<typeof loginSchema>>({
|
||||
resolver: zodResolver(loginSchema),
|
||||
});
|
||||
|
||||
// 폼 처리 로직
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. API 연동
|
||||
|
||||
```typescript
|
||||
// lib/api.ts
|
||||
class ApiClient {
|
||||
private baseURL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:9090";
|
||||
|
||||
async login(credentials: LoginCredentials): Promise<LoginResponse> {
|
||||
const response = await fetch(`${this.baseURL}/api/auth/login`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(credentials),
|
||||
credentials: "include", // 세션 쿠키 포함
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error("로그인 실패");
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 스타일링 가이드
|
||||
|
||||
#### 1. Tailwind CSS 클래스
|
||||
|
||||
```typescript
|
||||
// 일반적인 레이아웃
|
||||
<div className="flex items-center justify-center min-h-screen bg-slate-50">
|
||||
<div className="w-full max-w-md space-y-6">
|
||||
{/* 컨텐츠 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
// 카드 컴포넌트
|
||||
<div className="bg-white rounded-lg shadow-lg border border-slate-200 p-6">
|
||||
{/* 카드 내용 */}
|
||||
</div>
|
||||
|
||||
// 반응형 그리드
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{/* 그리드 아이템 */}
|
||||
</div>
|
||||
```
|
||||
|
||||
#### 2. CSS 변수 활용
|
||||
|
||||
```css
|
||||
/* globals.css */
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96%;
|
||||
--secondary-foreground: 222.2 84% 4.9%;
|
||||
}
|
||||
```
|
||||
|
||||
## 🔐 인증 시스템
|
||||
|
||||
### 세션 기반 인증 (기존 백엔드 호환)
|
||||
|
||||
```typescript
|
||||
// 로그인 프로세스
|
||||
1. 사용자가 로그인 폼 제출
|
||||
2. Next.js에서 백엔드 API 호출
|
||||
3. 백엔드에서 세션 생성 (기존 로직 사용)
|
||||
4. 프론트엔드에서 인증 상태 관리
|
||||
5. 보호된 라우트 접근 제어
|
||||
```
|
||||
|
||||
### 라우트 보호
|
||||
|
||||
```typescript
|
||||
// middleware.ts
|
||||
export function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
|
||||
// 공개 페이지
|
||||
const publicPaths = ["/login", "/"];
|
||||
if (publicPaths.includes(pathname)) return NextResponse.next();
|
||||
|
||||
// 인증 확인
|
||||
const sessionCookie = request.cookies.get("JSESSIONID");
|
||||
if (!sessionCookie) {
|
||||
return NextResponse.redirect(new URL("/login", request.url));
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 주요 기능
|
||||
|
||||
### 1. 대시보드
|
||||
|
||||
- **프로젝트 현황**: 진행 중인 프로젝트 상태 모니터링
|
||||
- **작업 요약**: 개인별 할당된 작업 목록
|
||||
- **알림 센터**: 중요한 업데이트 및 알림
|
||||
- **차트 및 그래프**: 프로젝트 진척도 시각화
|
||||
|
||||
### 2. 프로젝트 관리
|
||||
|
||||
- **프로젝트 생성/수정**: 새 프로젝트 등록 및 정보 관리
|
||||
- **팀 구성**: 프로젝트 멤버 할당 및 역할 관리
|
||||
- **마일스톤**: 주요 일정 및 목표 설정
|
||||
- **진행 상황 추적**: 실시간 프로젝트 진척도 모니터링
|
||||
|
||||
### 3. 제품 관리
|
||||
|
||||
- **제품 카탈로그**: 제품 정보 및 사양 관리
|
||||
- **BOM 관리**: Bill of Materials 구성 및 버전 관리
|
||||
- **설계 변경**: ECO/ECR 프로세스 관리
|
||||
- **문서 관리**: 기술 문서 및 도면 버전 제어
|
||||
|
||||
### 4. 사용자 관리
|
||||
|
||||
- **사용자 계정**: 계정 생성/수정/비활성화
|
||||
- **권한 관리**: 역할 기반 접근 제어 (RBAC)
|
||||
- **부서 관리**: 조직 구조 및 부서별 권한 설정
|
||||
- **감사 로그**: 사용자 활동 추적 및 보안 모니터링
|
||||
|
||||
## 🚢 배포 가이드
|
||||
|
||||
### 개발 환경
|
||||
|
||||
```bash
|
||||
# 프론트엔드 개발 서버
|
||||
cd frontend && npm run dev
|
||||
|
||||
# 백엔드 (Docker)
|
||||
docker-compose -f docker-compose.win.yml up -d plm-app
|
||||
```
|
||||
|
||||
### 운영 환경
|
||||
|
||||
```bash
|
||||
# 전체 서비스 배포
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# 무중단 배포 (blue-green)
|
||||
./deploy-production.sh
|
||||
```
|
||||
|
||||
### 환경 변수 설정
|
||||
|
||||
```bash
|
||||
# .env.local (Next.js)
|
||||
NEXT_PUBLIC_API_URL=http://localhost:9090
|
||||
NEXT_PUBLIC_APP_ENV=development
|
||||
|
||||
# docker-compose.win.yml (백엔드)
|
||||
DB_URL=jdbc:postgresql://db:5432/plm
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=secure_password
|
||||
```
|
||||
|
||||
## 🔧 문제 해결
|
||||
|
||||
### 자주 발생하는 문제
|
||||
|
||||
#### 1. 로그인 화면이 업데이트되지 않는 경우
|
||||
|
||||
```bash
|
||||
# 브라우저 캐시 클리어 후 다음 확인:
|
||||
# - Next.js 서버 재시작
|
||||
npm run dev
|
||||
|
||||
# - 올바른 URL 접속 확인
|
||||
# 올바름: http://localhost:9771/login
|
||||
# 잘못됨: http://localhost:9090/login.jsp
|
||||
```
|
||||
|
||||
#### 2. Docker 컨테이너 실행 오류
|
||||
|
||||
```bash
|
||||
# 포트 충돌 확인
|
||||
netstat -ano | findstr :9771
|
||||
netstat -ano | findstr :9090
|
||||
|
||||
# Docker 시스템 정리
|
||||
docker system prune -a
|
||||
docker-compose -f docker-compose.win.yml down --volumes
|
||||
```
|
||||
|
||||
#### 3. API 연결 오류
|
||||
|
||||
```bash
|
||||
# 백엔드 컨테이너 로그 확인
|
||||
docker-compose -f docker-compose.win.yml logs plm-app
|
||||
|
||||
# 네트워크 연결 확인
|
||||
curl http://localhost:9090/api/health
|
||||
```
|
||||
|
||||
### 개발자 도구
|
||||
|
||||
#### 브라우저 개발자 도구
|
||||
|
||||
- **Console**: JavaScript 오류 확인
|
||||
- **Network**: API 요청/응답 모니터링
|
||||
- **Application**: 세션 쿠키 확인
|
||||
|
||||
#### 로그 확인
|
||||
|
||||
```bash
|
||||
# Next.js 개발 서버 로그
|
||||
npm run dev
|
||||
|
||||
# 백엔드 애플리케이션 로그
|
||||
docker-compose -f docker-compose.win.yml logs -f plm-app
|
||||
|
||||
# 데이터베이스 로그
|
||||
docker-compose -f docker-compose.win.yml logs -f db
|
||||
```
|
||||
|
||||
## 📈 성능 최적화
|
||||
|
||||
### Next.js 최적화
|
||||
|
||||
- **이미지 최적화**: Next.js Image 컴포넌트 사용
|
||||
- **코드 분할**: 동적 임포트로 번들 크기 최소화
|
||||
- **서버 사이드 렌더링**: 초기 로딩 속도 개선
|
||||
- **정적 생성**: 변경되지 않는 페이지 사전 생성
|
||||
|
||||
### 백엔드 최적화
|
||||
|
||||
- **데이터베이스 인덱스**: 자주 조회되는 필드 인덱싱
|
||||
- **쿼리 최적화**: N+1 문제 해결
|
||||
- **캐싱**: Redis를 통한 세션 및 데이터 캐싱
|
||||
- **리소스 최적화**: JVM 메모리 튜닝
|
||||
|
||||
## 🤝 기여 가이드
|
||||
|
||||
### 코드 컨벤션
|
||||
|
||||
- **TypeScript**: 엄격한 타입 정의 사용
|
||||
- **ESLint**: 코드 품질 유지.
|
||||
- **Prettier**: 일관된 코드 포맷팅
|
||||
- **커밋 메시지**: Conventional Commits 규칙 준수
|
||||
|
||||
### 브랜치 전략
|
||||
|
||||
```bash
|
||||
main # 운영 환경 배포 브랜치
|
||||
develop # 개발 환경 통합 브랜치
|
||||
feature/* # 기능 개발 브랜치
|
||||
hotfix/* # 긴급 수정 브랜치
|
||||
```
|
||||
|
||||
### Pull Request 프로세스
|
||||
|
||||
1. 기능 브랜치에서 개발
|
||||
2. 테스트 코드 작성
|
||||
3. PR 생성 및 코드 리뷰
|
||||
4. 승인 후 develop 브랜치에 병합
|
||||
|
||||
## 📞 지원 및 문의
|
||||
|
||||
- **개발팀 문의**: 내부 Slack 채널 `#plm-support`
|
||||
- **버그 리포트**: GitHub Issues
|
||||
- **기능 요청**: Product Owner와 협의
|
||||
- **긴급 상황**: 개발팀 직접 연락
|
||||
|
||||
---
|
||||
|
||||
## 📝 변경 이력
|
||||
|
||||
### v2.0.0 (2025년 1월)
|
||||
|
||||
- ✅ JSP → Next.js 14 완전 전환
|
||||
- ✅ shadcn/ui 디자인 시스템 도입
|
||||
- ✅ TypeScript 타입 안전성 강화
|
||||
- ✅ 반응형 디자인 적용
|
||||
- ✅ WACE 브랜딩 적용
|
||||
|
||||
### v1.x (레거시)
|
||||
|
||||
- ❌ JSP + jQuery 기반 (사용 중단)
|
||||
- ❌ 데스크톱 전용 UI
|
||||
- ❌ 제한적인 확장성
|
||||
|
||||
**🎉 현재 버전 2.0.0에서는 완전히 새로운 사용자 경험을 제공합니다!**
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<Context docBase="ilshin" path="/" reloadable="true" source="org.eclipse.jst.jee.server:ilshin">
|
||||
<Resource auth="Container" driverClassName="org.postgresql.Driver" maxActive="100" maxIdle="10" maxWait="-1" name="plm" password="admin0909!!" type="javax.sql.DataSource" url="jdbc:postgresql://211.224.136.4:5432/ilshin" username="postgres"/>
|
||||
</Context>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
Manifest-Version: 1.0
|
||||
Class-Path:
|
||||
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="ko" xml:lang="ko">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>네이버 :: Smart Editor 2 ™</title>
|
||||
<script type="text/javascript" src="./js/HuskyEZCreator.js" charset="utf-8"></script>
|
||||
</head>
|
||||
<body>
|
||||
<form action="sample.php" method="post">
|
||||
<textarea name="ir1" id="ir1" rows="10" cols="100" style="width:766px; height:412px; display:none;"></textarea>
|
||||
<!--textarea name="ir1" id="ir1" rows="10" cols="100" style="width:100%; height:412px; min-width:610px; display:none;"></textarea-->
|
||||
<p>
|
||||
<input type="button" onclick="pasteHTML();" value="본문에 내용 넣기" />
|
||||
<input type="button" onclick="showHTML();" value="본문 내용 가져오기" />
|
||||
<input type="button" onclick="submitContents(this);" value="서버로 내용 전송" />
|
||||
<input type="button" onclick="setDefaultFont();" value="기본 폰트 지정하기 (궁서_24)" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
var oEditors = [];
|
||||
|
||||
// 추가 글꼴 목록
|
||||
//var aAdditionalFontSet = [["MS UI Gothic", "MS UI Gothic"], ["Comic Sans MS", "Comic Sans MS"],["TEST","TEST"]];
|
||||
|
||||
nhn.husky.EZCreator.createInIFrame({
|
||||
oAppRef: oEditors,
|
||||
elPlaceHolder: "ir1",
|
||||
sSkinURI: "SmartEditor2Skin.html",
|
||||
htParams : {
|
||||
bUseToolbar : true, // 툴바 사용 여부 (true:사용/ false:사용하지 않음)
|
||||
bUseVerticalResizer : true, // 입력창 크기 조절바 사용 여부 (true:사용/ false:사용하지 않음)
|
||||
bUseModeChanger : true, // 모드 탭(Editor | HTML | TEXT) 사용 여부 (true:사용/ false:사용하지 않음)
|
||||
//aAdditionalFontList : aAdditionalFontSet, // 추가 글꼴 목록
|
||||
fOnBeforeUnload : function(){
|
||||
//alert("완료!");
|
||||
}
|
||||
}, //boolean
|
||||
fOnAppLoad : function(){
|
||||
//예제 코드
|
||||
//oEditors.getById["ir1"].exec("PASTE_HTML", ["로딩이 완료된 후에 본문에 삽입되는 text입니다."]);
|
||||
},
|
||||
fCreator: "createSEditor2"
|
||||
});
|
||||
|
||||
function pasteHTML() {
|
||||
var sHTML = "<span style='color:#FF0000;'>이미지도 같은 방식으로 삽입합니다.<\/span>";
|
||||
oEditors.getById["ir1"].exec("PASTE_HTML", [sHTML]);
|
||||
}
|
||||
|
||||
function showHTML() {
|
||||
var sHTML = oEditors.getById["ir1"].getIR();
|
||||
alert(sHTML);
|
||||
}
|
||||
|
||||
function submitContents(elClickedObj) {
|
||||
oEditors.getById["ir1"].exec("UPDATE_CONTENTS_FIELD", []); // 에디터의 내용이 textarea에 적용됩니다.
|
||||
|
||||
// 에디터의 내용에 대한 값 검증은 이곳에서 document.getElementById("ir1").value를 이용해서 처리하면 됩니다.
|
||||
|
||||
try {
|
||||
elClickedObj.form.submit();
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function setDefaultFont() {
|
||||
var sDefaultFont = '궁서';
|
||||
var nFontSize = 24;
|
||||
oEditors.getById["ir1"].setDefaultFont(sDefaultFont, nFontSize);
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
@charset "UTF-8";
|
||||
/* NHN Web Standardization Team (http://html.nhndesign.com/) HHJ 090226 */
|
||||
/* COMMON */
|
||||
body,#smart_editor2,#smart_editor2 p,#smart_editor2 h1,#smart_editor2 h2,#smart_editor2 h3,#smart_editor2 h4,#smart_editor2 h5,#smart_editor2 h6,#smart_editor2 ul,#smart_editor2 ol,#smart_editor2 li,#smart_editor2 dl,#smart_editor2 dt,#smart_editor2 dd,#smart_editor2 table,#smart_editor2 th,#smart_editor2 td,#smart_editor2 form,#smart_editor2 fieldset,#smart_editor2 legend,#smart_editor2 input,#smart_editor2 textarea,#smart_editor2 button,#smart_editor2 select{margin:0;padding:0}
|
||||
#smart_editor2,#smart_editor2 h1,#smart_editor2 h2,#smart_editor2 h3,#smart_editor2 h4,#smart_editor2 h5,#smart_editor2 h6,#smart_editor2 input,#smart_editor2 textarea,#smart_editor2 select,#smart_editor2 table,#smart_editor2 button{font-family:'돋움',Dotum,Helvetica,sans-serif;font-size:12px;color:#666}
|
||||
#smart_editor2 span,#smart_editor2 em{font-size:12px}
|
||||
#smart_editor2 em,#smart_editor2 address{font-style:normal}
|
||||
#smart_editor2 img,#smart_editor2 fieldset{border:0}
|
||||
#smart_editor2 hr{display:none}
|
||||
#smart_editor2 ol,#smart_editor2 ul{list-style:none}
|
||||
#smart_editor2 button{border:0;background:none;font-size:11px;vertical-align:top;cursor:pointer}
|
||||
#smart_editor2 button span,#smart_editor2 button em{visibility:hidden;overflow:hidden;position:absolute;top:0;font-size:0;line-height:0}
|
||||
#smart_editor2 legend,#smart_editor2 .blind{visibility:hidden;overflow:hidden;position:absolute;width:0;height:0;font-size:0;line-height:0}
|
||||
#smart_editor2 .input_ty1{height:14px;margin:0;padding:4px 2px 0 4px;border:1px solid #c7c7c7;font-size:11px;color:#666}
|
||||
#smart_editor2 a:link,#smart_editor2 a:visited,#smart_editor2 a:active,#smart_editor2 a:focus{color:#666;text-decoration:none}
|
||||
#smart_editor2 a:hover{color:#666;text-decoration:underline}
|
||||
/* LAYOUT */
|
||||
#smart_editor2 .se2_header{margin:10px 0 29px 0}
|
||||
#smart_editor2 .se2_bi{float:left;width:93px;height:20px;margin:0;padding:0;background:url("../img/ko_KR/btn_set.png?130306") -343px -358px no-repeat;font-size:0;line-height:0;text-indent:-10000px;vertical-align:middle}
|
||||
#smart_editor2 .se2_allhelp{display:inline-block;width:18px;height:18px;padding:0;background:url("../img/ko_KR/btn_set.png?130306") -437px -358px no-repeat;font-size:0;line-height:0;text-indent:-10000px;vertical-align:middle}
|
||||
#smart_editor2 #smart_editor2_content{border:1px solid #b5b5b5}
|
||||
#smart_editor2 .se2_tool{overflow:visible;position:relative;z-index:25}
|
||||
/* EDITINGAREA */
|
||||
#smart_editor2 .se2_input_area{position:relative;z-index:22;height:400px;margin:0;padding:0;*zoom:1}
|
||||
#smart_editor2 .se2_input_wysiwyg,#smart_editor2 .se2_input_syntax{display:block;overflow:auto;width:100%;height:100%;margin:0;*margin:-1px 0 0 0;border:0}
|
||||
/* EDITINGMODE */
|
||||
#smart_editor2 .se2_conversion_mode{position:relative;height:15px;padding-top:1px;border-top:1px solid #b5b5b5;background:url("../img/icon_set.gif") 0 -896px repeat-x}
|
||||
#smart_editor2 .se2_inputarea_controller{display:block;clear:both;position:relative;width:100%;height:15px;text-align:center;cursor:n-resize}
|
||||
#smart_editor2 .se2_inputarea_controller span,#smart_editor2 .controller_on span{background:url("../img/ico_extend.png") no-repeat}
|
||||
#smart_editor2 .se2_inputarea_controller span{position:static;display:inline-block;visibility:visible;overflow:hidden;height:15px;padding-left:11px;background-position:0 2px;color:#888;font-size:11px;letter-spacing:-1px;line-height:16px;white-space:nowrap}
|
||||
* + html #smart_editor2 .se2_inputarea_controller span{line-height:14px}
|
||||
#smart_editor2 .controller_on span{background-position:0 -21px;color:#249c04}
|
||||
#smart_editor2 .ly_controller{display:block;position:absolute;bottom:2px;left:50%;width:287px;margin-left:-148px;padding:8px 0 7px 9px;border:1px solid #827f7c;background:#fffdef}
|
||||
#smart_editor2 .ly_controller p{color:#666;font-size:11px;letter-spacing:-1px;line-height:11px}
|
||||
#smart_editor2 .ly_controller .bt_clse,#smart_editor2 .ly_controller .ic_arr{position:absolute;background:url("../img/ico_extend.png") no-repeat}
|
||||
#smart_editor2 .ly_controller .bt_clse{top:5px;right:4px;width:14px;height:15px;background-position:1px -43px}
|
||||
#smart_editor2 .ly_controller .ic_arr{top:25px;left:50%;width:10px;height:6px;margin-left:-5px;background-position:0 -65px}
|
||||
#smart_editor2 .se2_converter{float:left;position:absolute;top:-1px;right:3px;z-index:20}
|
||||
#smart_editor2 .se2_converter li{float:left}
|
||||
#smart_editor2 .se2_converter .se2_to_editor{width:59px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") 0 -85px no-repeat;vertical-align:top}
|
||||
#smart_editor2 .se2_converter .se2_to_html{width:59px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") -59px -70px no-repeat;vertical-align:top}
|
||||
#smart_editor2 .se2_converter .se2_to_text{width:60px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") -417px -466px no-repeat;vertical-align:top}
|
||||
#smart_editor2 .se2_converter .active .se2_to_editor{width:59px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") 0 -70px no-repeat;vertical-align:top}
|
||||
#smart_editor2 .se2_converter .active .se2_to_html{width:59px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") -59px -85px no-repeat;vertical-align:top}
|
||||
#smart_editor2 .se2_converter .active .se2_to_text{width:60px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") -417px -481px no-repeat;vertical-align:top}
|
||||
/* EDITINGAREA_HTMLSRC */
|
||||
#smart_editor2 .off .ico_btn,#smart_editor2 .off .se2_more,#smart_editor2 .off .se2_more2,#smart_editor2 .off .se2_font_family,#smart_editor2 .off .se2_font_size,#smart_editor2 .off .se2_bold,#smart_editor2 .off .se2_underline,#smart_editor2 .off .se2_italic,#smart_editor2 .off .se2_tdel,#smart_editor2 .off .se2_fcolor,#smart_editor2 .off .se2_fcolor_more,#smart_editor2 .off .se2_bgcolor,#smart_editor2 .off .se2_bgcolor_more,#smart_editor2 .off .se2_left,#smart_editor2 .off .se2_center,#smart_editor2 .off .se2_right,#smart_editor2 .off .se2_justify,#smart_editor2 .off .se2_ol,#smart_editor2 .off .se2_ul,#smart_editor2 .off .se2_indent,#smart_editor2 .off .se2_outdent,#smart_editor2 .off .se2_lineheight,#smart_editor2 .off .se2_del_style,#smart_editor2 .off .se2_blockquote,#smart_editor2 .off .se2_summary,#smart_editor2 .off .se2_footnote,#smart_editor2 .off .se2_url,#smart_editor2 .off .se2_emoticon,#smart_editor2 .off .se2_character,#smart_editor2 .off .se2_table,#smart_editor2 .off .se2_find,#smart_editor2 .off .se2_spelling,#smart_editor2 .off .se2_sup,#smart_editor2 .off .se2_sub,#smart_editor2 .off .se2_text_tool_more,#smart_editor2 .off .se2_new,#smart_editor2 .off .selected_color,#smart_editor2 .off .se2_lineSticker{-ms-filter:alpha(opacity=50);opacity:.5;cursor:default;filter:alpha(opacity=50)}
|
||||
/* LAYER */
|
||||
#smart_editor2 .se2_text_tool .se2_layer{display:none;float:left;position:absolute;top:20px;left:0;z-index:50;margin:0;padding:0;border:1px solid #bcbbbb;background:#fafafa}
|
||||
#smart_editor2 .se2_text_tool li.active{z-index:50}
|
||||
#smart_editor2 .se2_text_tool .active .se2_layer{display:block}
|
||||
#smart_editor2 .se2_text_tool .active li .se2_layer{display:none}
|
||||
#smart_editor2 .se2_text_tool .active .active .se2_layer{display:block}
|
||||
#smart_editor2 .se2_text_tool .se2_layer .se2_in_layer{float:left;margin:0;padding:0;border:1px solid #fff;background:#fafafa}
|
||||
/* TEXT_TOOLBAR */
|
||||
#smart_editor2 .se2_text_tool{position:relative;clear:both;z-index:30;padding:4px 0 4px 3px;background:#f4f4f4 url("../img/bg_text_tool.gif") 0 0 repeat-x;border-bottom:1px solid #b5b5b5;*zoom:1}
|
||||
#smart_editor2 .se2_text_tool:after{content:"";display:block;clear:both}
|
||||
#smart_editor2 .se2_text_tool ul{float:left;display:inline;margin-right:3px;padding-left:1px;white-space:nowrap}
|
||||
#smart_editor2 .se2_text_tool li{_display:inline;float:left;position:relative;z-index:30}
|
||||
#smart_editor2 .se2_text_tool button,#smart_editor2 .se2_multy .se2_icon{width:21px;height:21px;background:url("../img/ko_KR/text_tool_set.png?140317") no-repeat;vertical-align:top}
|
||||
#smart_editor2 .se2_text_tool .se2_font_type{position:relative}
|
||||
#smart_editor2 .se2_text_tool .se2_font_type li{margin-left:3px}
|
||||
#smart_editor2 .se2_text_tool .se2_font_type button{text-align:left}
|
||||
#smart_editor2 .se2_text_tool .se2_font_type button.se2_font_family span,#smart_editor2 .se2_text_tool .se2_font_type button.se2_font_size span{display:inline-block;visibility:visible;position:static;width:52px;height:20px;padding:0 0 0 6px;font-size:12px;line-height:20px;*line-height:22px;color:#333;*zoom:1}
|
||||
#smart_editor2 .se2_text_tool .se2_multy{position:absolute;top:0;right:0;padding-left:0;margin-right:0;white-space:nowrap;border-left:1px solid #e0dedf}
|
||||
#smart_editor2 .se2_text_tool .se2_multy .se2_mn{float:left;white-space:nowrap}
|
||||
#smart_editor2 .se2_text_tool .se2_multy button{background-image:none;width:47px}
|
||||
#smart_editor2 .se2_text_tool .se2_multy .se2_icon{display:inline-block;visibility:visible;overflow:visible;position:static;width:16px;height:29px;margin:-1px 2px 0 -1px;background-position:0 -132px;line-height:30px;vertical-align:top}
|
||||
#smart_editor2 .se2_text_tool .se2_multy button,#smart_editor2 .se2_text_tool .se2_multy button span{height:29px;line-height:29px}
|
||||
#smart_editor2 .se2_text_tool .se2_map .se2_icon{background-position:-29px -132px}
|
||||
#smart_editor2 .se2_text_tool button span.se2_mntxt{display:inline-block;visibility:visible;overflow:visible;_overflow-y:hidden;position:relative;*margin-right:-1px;width:auto;height:29px;font-weight:normal;font-size:11px;line-height:30px;*line-height:29px;_line-height:30px;color:#444;letter-spacing:-1px;vertical-align:top}
|
||||
#smart_editor2 .se2_text_tool .se2_multy .se2_photo{margin-right:1px}
|
||||
#smart_editor2 .se2_text_tool .se2_multy .hover .ico_btn{background:#e8e8e8}
|
||||
#smart_editor2 .se2_text_tool .se2_multy .se2_mn.hover{background:#e0dedf}
|
||||
/* TEXT_TOOLBAR : ROUNDING */
|
||||
#smart_editor2 ul li.first_child button span.tool_bg,#smart_editor2 ul li.last_child button span.tool_bg,#smart_editor2 ul li.single_child button span.tool_bg{visibility:visible;height:21px}
|
||||
#smart_editor2 ul li.first_child button span.tool_bg{left:-1px;width:3px;background:url("../img/bg_button_left.gif?20121228") no-repeat}
|
||||
#smart_editor2 ul li.last_child button span.tool_bg{right:0px;_right:-1px;width:2px;background:url("../img/bg_button_right.gif") no-repeat}
|
||||
#smart_editor2 ul li.single_child{padding-right:1px}
|
||||
#smart_editor2 ul li.single_child button span.tool_bg{left:0;background:url("../img/bg_button.gif?20121228") no-repeat;width:22px}
|
||||
#smart_editor2 div.se2_text_tool ul li.hover button span.tool_bg{background-position:0 -21px}
|
||||
#smart_editor2 div.se2_text_tool ul li.active button span.tool_bg,#smart_editor2 div.se2_text_tool ul li.active li.active button span.tool_bg{background-position:0 -42px}
|
||||
#smart_editor2 div.se2_text_tool ul li.active li button span.tool_bg{background-position:0 0}
|
||||
/* TEXT_TOOLBAR : SUB_MENU */
|
||||
#smart_editor2 .se2_sub_text_tool{display:none;position:absolute;top:20px;left:0;z-index:40;width:auto;height:29px;padding:0 4px 0 0;border:1px solid #b5b5b5;border-top:1px solid #9a9a9a;background:#f4f4f4}
|
||||
#smart_editor2 .active .se2_sub_text_tool{display:block}
|
||||
#smart_editor2 .se2_sub_text_tool ul{float:left;height:25px;margin:0;padding:4px 0 0 4px}
|
||||
/* TEXT_TOOLBAR : SUB_MENU_SIZE */
|
||||
#smart_editor2 .se2_sub_step1{width:88px}
|
||||
#smart_editor2 .se2_sub_step2{width:199px}
|
||||
#smart_editor2 .se2_sub_step2_1{width:178px}
|
||||
/* TEXT_TOOLBAR : BUTTON */
|
||||
#smart_editor2 .se2_text_tool .se2_font_family{width:70px;height:21px;background-position:0 -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_font_family{background-position:0 -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_font_family{background-position:0 -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_font_size{width:45px;height:21px;background-position:-70px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_font_size{background-position:-70px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_font_size{background-position:-70px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_bold{background-position:-115px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_bold{background-position:-115px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_bold{background-position:-115px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_underline{background-position:-136px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_underline{background-position:-136px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_underline{background-position:-136px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_italic{background-position:-157px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_italic{background-position:-157px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_italic{background-position:-157px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_tdel{background-position:-178px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_tdel{background-position:-178px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_tdel{background-position:-178px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_fcolor{position:relative;background-position:-199px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_fcolor{background-position:-199px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_fcolor{background-position:-199px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_fcolor_more{background-position:-220px -10px;width:10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_fcolor_more{background-position:-220px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_fcolor_more{background-position:-220px -103px}
|
||||
#smart_editor2 .se2_text_tool .selected_color{position:absolute;top:14px;left:5px;width:11px;height:3px;font-size:0}
|
||||
#smart_editor2 .se2_text_tool .se2_ol,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .se2_ol{background-position:-345px -10px}
|
||||
#smart_editor2 .se2_text_tool .se2_ul,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .se2_ul{background-position:-366px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_ol,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .hover .se2_ol{background-position:-345px -72px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_ul,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .hover .se2_ul{background-position:-366px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_ol,#smart_editor2 .se2_text_tool .active .active .se2_ol{background-position:-345px -103px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_ul,#smart_editor2 .se2_text_tool .active .active .se2_ul{background-position:-366px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_indent,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .se2_indent{background-position:-408px -10px}
|
||||
#smart_editor2 .se2_text_tool .se2_outdent,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .se2_outdent{background-position:-387px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_indent,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .hover .se2_indent{background-position:-408px -72px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_outdent,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .hover .se2_outdent{background-position:-387px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_indent,#smart_editor2 .se2_text_tool .active .active .se2_indent{background-position:-408px -103px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_outdent,#smart_editor2 .se2_text_tool .active .active .se2_outdent{background-position:-387px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_lineheight{background-position:-429px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_lineheight{background-position:-429px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_lineheight{background-position:-429px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_url{background-position:-513px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_url{background-position:-513px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_url{background-position:-513px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_bgcolor{position:relative;background-position:-230px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_bgcolor{background-position:-230px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_bgcolor{background-position:-230px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_bgcolor_more{background-position:-251px -10px;width:10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_bgcolor_more{background-position:-251px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_bgcolor_more{background-position:-251px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_left{background-position:-261px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_left{background-position:-261px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_left{background-position:-261px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_center{background-position:-282px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_center{background-position:-282px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_center{background-position:-282px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_right{background-position:-303px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_right{background-position:-303px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_right{background-position:-303px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_justify{background-position:-324px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_justify{background-position:-324px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_justify{background-position:-324px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_blockquote{background-position:-471px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_blockquote{background-position:-471px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_blockquote{background-position:-471px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_character{background-position:-555px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_character{background-position:-555px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_character{background-position:-555px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_table{background-position:-576px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_table{background-position:-576px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_table{background-position:-576px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_find{background-position:-597px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_find{background-position:-597px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_find{background-position:-597px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_sup{background-position:-660px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_sup{background-position:-660px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_sup{background-position:-660px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_sub{background-position:-681px -10px}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_sub{background-position:-681px -72px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_sub{background-position:-681px -103px}
|
||||
#smart_editor2 .se2_text_tool .se2_text_tool_more{background-position:0 -41px;width:13px}
|
||||
#smart_editor2 .se2_text_tool .se2_text_tool_more span.tool_bg{background:none}
|
||||
#smart_editor2 .se2_text_tool .hover .se2_text_tool_more{background-position:-13px -41px}
|
||||
#smart_editor2 .se2_text_tool .active .se2_text_tool_more{background-position:-26px -41px}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
@charset "UTF-8";
|
||||
/* NHN Web Standardization Team (http://html.nhndesign.com/) HHJ 090226 */
|
||||
/* COMMON */
|
||||
body,.se2_inputarea{margin:0;padding:0;font-family:'돋움',Dotum,Helvetica,Sans-serif;font-size:12px;line-height:1.5}
|
||||
/* body,.se2_inputarea,.se2_inputarea th,.se2_inputarea td{margin:0;padding:0;font-family:'돋움',Dotum,Helvetica,Sans-serif;font-size:12px;line-height:1.5;color:#666} */
|
||||
.se2_inputarea p,.se2_inputarea br{margin:0;padding:0}
|
||||
.se2_inputarea{margin:15px;word-wrap:break-word;*word-wrap:normal;*word-break:break-all}
|
||||
.se2_inputarea td{word-break:break-all}
|
||||
.se2_inputarea_890{width:741px;margin:20px 0 10px 64px}
|
||||
.se2_inputarea_698{width:548px;margin:20px 0 10px 64px}
|
||||
/* TEXT_TOOLBAR : QUOTE */
|
||||
.se2_quote1{margin:0 0 30px 20px;padding:0 8px;border-left:2px solid #ccc;color:#888}
|
||||
.se2_quote2{margin:0 0 30px 13px;padding:0 8px 0 16px;background:url("../img/bg_quote2.gif") 0 3px no-repeat;color:#888}
|
||||
.se2_quote3{margin:0 0 30px;padding:12px 10px 11px;border:1px dashed #ccc;color:#888}
|
||||
.se2_quote4{margin:0 0 30px;padding:12px 10px 11px;border:1px dashed #66b246;color:#888}
|
||||
.se2_quote5{margin:0 0 30px;padding:12px 10px 11px;border:1px dashed #ccc;background:#fafafa;color:#888}
|
||||
.se2_quote6{margin:0 0 30px;padding:12px 10px 11px;border:1px solid #e5e5e5;color:#888}
|
||||
.se2_quote7{margin:0 0 30px;padding:12px 10px 11px;border:1px solid #66b246;color:#888}
|
||||
.se2_quote8{margin:0 0 30px;padding:12px 10px 11px;border:1px solid #e5e5e5;background:#fafafa;color:#888}
|
||||
.se2_quote9{margin:0 0 30px;padding:12px 10px 11px;border:2px solid #e5e5e5;color:#888}
|
||||
.se2_quote10{margin:0 0 30px;padding:12px 10px 11px;border:2px solid #e5e5e5;background:#fafafa;color:#888}
|
||||
|
|
@ -0,0 +1,462 @@
|
|||
@charset "UTF-8";
|
||||
/* NHN Web Standardization Team (http://html.nhndesign.com/) HHJ 090226 */
|
||||
/* TEXT_TOOLBAR : FONTNAME */
|
||||
#smart_editor2 .se2_tool .se2_l_font_fam{width:202px;margin:0;padding:0}
|
||||
#smart_editor2 .se2_tool .se2_l_font_fam li{display:block;width:202px;height:21px;margin:0;padding:0;color:#333;cursor:pointer}
|
||||
#smart_editor2 .se2_l_font_fam .hover,#smart_editor2 .se2_l_font_fam .active{background:#ebebeb}
|
||||
#smart_editor2 .se2_l_font_fam button{width:200px;height:21px;margin:0;padding:2px 0 2px 0px;background:none;text-align:left}
|
||||
#smart_editor2 .se2_l_font_fam button span{display:block;visibility:visible;overflow:visible;position:relative;top:auto;left:auto;width:auto;height:auto;margin:0 0 0 4px;padding:0;font-size:12px;line-height:normal;color:#333}
|
||||
#smart_editor2 .se2_l_font_fam button span span{display:inline;visibility:visible;overflow:visible;width:auto;height:auto;margin:0 0 0 4px;font-family:Verdana;font-size:12px;line-height:14px;color:#888}
|
||||
#smart_editor2 .se2_l_font_fam button span em{visibility:visible;overflow:auto;position:static;width:auto;height:auto;margin-right:-4px;font-size:12px;color:#888}
|
||||
#smart_editor2 .se2_l_font_fam .se2_division{width:162px;height:2px !important;margin:1px 0 1px 0px;border:0;background:url("../img/bg_line1.gif") 0 0 repeat-x;font-size:0;cursor:default}
|
||||
/* TEXT_TOOLBAR : FONTSIZE */
|
||||
#smart_editor2 .se2_tool .se2_l_font_size{width:302px;margin:0;padding:0}
|
||||
#smart_editor2 .se2_tool .se2_l_font_size li{width:302px;margin:0;padding:0;color:#333;cursor:pointer}
|
||||
#smart_editor2 .se2_l_font_size .hover,#smart_editor2 .se2_l_font_size .active{background:#ebebeb}
|
||||
#smart_editor2 .se2_l_font_size button{width:300px;height:auto;margin:0;padding:2px 0 1px 0px;*padding:4px 0 1px 0px;background:none;text-align:left}
|
||||
#smart_editor2 .se2_l_font_size button span{display:block;visibility:visible;overflow:visible;position:relative;top:auto;left:auto;width:auto;height:auto;margin:0 0 0 4px;padding:0;line-height:normal;color:#373737;letter-spacing:0px}
|
||||
#smart_editor2 .se2_l_font_size button span span{display:inline;margin:0 0 0 5px;padding:0}
|
||||
#smart_editor2 .se2_l_font_size span em{visibility:visible;overflow:auto;position:static;width:auto;height:auto;color:#888}
|
||||
/* TEXT_TOOLBAR : FONTCOLOR */
|
||||
#smart_editor2 .se2_palette{float:left;position:relative;width:225px;margin:0;padding:11px 0 10px 0}
|
||||
#smart_editor2 .se2_palette .se2_pick_color{_display:inline;float:left;clear:both;width:205px;margin:0 0 0 11px;padding:0}
|
||||
#smart_editor2 .se2_palette .se2_pick_color li{float:left;width:12px;height:12px;margin:0;padding:0}
|
||||
#smart_editor2 .se2_palette .se2_pick_color li button{width:11px;height:11px;border:0}
|
||||
#smart_editor2 .se2_palette .se2_pick_color li button span{display:block;visibility:visible;overflow:visible;position:absolute;top:1px;left:1px;width:11px;height:11px}
|
||||
#smart_editor2 .se2_palette .se2_pick_color li button span span{visibility:hidden;overflow:hidden;position:absolute;top:0;left:0;width:0;height:0}
|
||||
#smart_editor2 .se2_palette .se2_pick_color .hover button,#smart_editor2 .se2_palette .se2_pick_color .active button{width:11px;height:11px;border:1px solid #666}
|
||||
#smart_editor2 .se2_palette .se2_pick_color .hover span,#smart_editor2 .se2_palette .se2_pick_color .active span{width:7px;height:7px;border:1px solid #fff}
|
||||
#smart_editor2 .se2_palette .se2_view_more{_display:inline;float:left;width:46px;height:23px;margin:1px 0 0 1px;background:url("../img/ko_KR/btn_set.png?130306") 0 -47px no-repeat}
|
||||
#smart_editor2 .se2_palette .se2_view_more2{_display:inline;float:left;width:46px;height:23px;margin:1px 0 0 1px;background:url("../img/ko_KR/btn_set.png?130306") 0 -24px no-repeat}
|
||||
#smart_editor2 .se2_palette h4{_display:inline;float:left;width:203px;margin:9px 0 0 11px;padding:10px 0 4px 0;background:url("../img/bg_line1.gif") repeat-x;font-weight:normal;font-size:12px;line-height:14px;color:#333;letter-spacing:-1px}
|
||||
#smart_editor2 .se2_palette2{float:left;_float:none;width:214px;margin:9px 0 0 0;padding:11px 0 0 11px;background:url("../img/bg_line1.gif") repeat-x}
|
||||
#smart_editor2 .se2_palette2 .se2_color_set{float:left}
|
||||
#smart_editor2 .se2_palette2 .se2_selected_color{_display:inline;float:left;width:83px;height:18px;margin:0;border:1px solid #c7c7c7;background:#fff}
|
||||
#smart_editor2 .se2_palette2 .se2_selected_color span{_display:inline;float:left;width:79px;height:14px;margin:2px}
|
||||
#smart_editor2 .se2_palette2 .input_ty1{_display:inline;float:left;width:67px;height:16px;margin:0 3px 0 3px;padding:2px 2px 0 4px;font-family:tahoma;font-size:11px}
|
||||
#smart_editor2 .se2_palette2 button.se2_btn_insert{float:left;width:35px;height:21px;margin-left:2px;padding:0;background:url("../img/ko_KR/btn_set.png?130306") -80px 0 no-repeat}
|
||||
#smart_editor2 .se2_gradation1{float:left;_float:none;width:201px;height:128px;margin:4px 0 0 0;border:1px solid #c7c7c7;cursor:crosshair}
|
||||
#smart_editor2 .se2_gradation2{float:left;_float:none;width:201px;height:10px;margin:4px 0 1px 0;border:1px solid #c7c7c7;cursor:crosshair}
|
||||
/* TEXT_TOOLBAR : BGCOLOR */
|
||||
#smart_editor2 .se2_palette_bgcolor{width:225px;margin:11px 0 0;padding:0}
|
||||
#smart_editor2 .se2_palette_bgcolor .se2_background{width:205px;margin:0 11px 0 11px}
|
||||
#smart_editor2 .se2_palette_bgcolor .se2_background li{width:68px;height:20px}
|
||||
#smart_editor2 .se2_palette_bgcolor .se2_background button{width:67px;height:19px;border:0}
|
||||
#smart_editor2 .se2_palette_bgcolor .se2_background span{left:0;display:block;visibility:visible;overflow:visible;width:65px;height:17px;padding:0}
|
||||
#smart_editor2 .se2_palette_bgcolor .se2_background span span{display:block;visibility:visible;overflow:visible;width:64px;height:16px;padding:3px 0 0 3px;font-size:11px;line-height:14px;text-align:left}
|
||||
#smart_editor2 .se2_palette_bgcolor .se2_background .hover span{width:65px;height:17px;border:1px solid #666}
|
||||
#smart_editor2 .se2_palette_bgcolor .se2_background .hover span span{width:62px;height:14px;padding:1px 0 0 1px;border:1px solid #fff}
|
||||
/* TEXT_TOOLBAR : LINEHEIGHT */
|
||||
#smart_editor2 .se2_l_line_height{width:107px;margin:0;padding:0}
|
||||
#smart_editor2 .se2_l_line_height li{width:107px;margin:0;padding:0;border-top:0;border-bottom:0;color:#333;cursor:pointer}
|
||||
#smart_editor2 .se2_l_line_height .hover{background:#ebebeb}
|
||||
#smart_editor2 .se2_l_line_height button{width:105px;height:19px;margin:0;padding:3px 0 2px 0px;background:none;text-align:left}
|
||||
#smart_editor2 .se2_l_line_height button span{visibility:visible;overflow:visible;position:relative;width:auto;height:auto;margin:0;padding:0 0 0 15px;font-size:12px;line-height:normal;color:#373737}
|
||||
#smart_editor2 .se2_l_line_height li button.active span{background:url("../img/icon_set.gif") 5px -30px no-repeat}
|
||||
#smart_editor2 .se2_l_line_height_user{clear:both;width:83px;margin:5px 0 0 12px;padding:10px 0 0 0;_padding:11px 0 0 0;background:url("../img/bg_line1.gif") repeat-x}
|
||||
#smart_editor2 .se2_l_line_height_user h3{margin:0 0 4px 0;_margin:0 0 2px -1px;padding:0;line-height:14px;color:#000;letter-spacing:-1px}
|
||||
#smart_editor2 .se2_l_line_height_user .bx_input{display:block;position:relative;width:83px}
|
||||
#smart_editor2 .se2_l_line_height_user .btn_up{position:absolute;top:2px;*top:3px;left:68px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -54px no-repeat}
|
||||
#smart_editor2 .se2_l_line_height_user .btn_down{position:absolute;top:10px;*top:11px;left:68px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -62px no-repeat}
|
||||
#smart_editor2 .se2_l_line_height_user .btn_area{margin:5px 0 10px 0}
|
||||
#smart_editor2 .se2_tool .btn_area .se2_btn_apply3{width:41px;height:24px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat}
|
||||
#smart_editor2 .se2_tool .btn_area .se2_btn_cancel3{width:39px;height:24px;margin-left:3px;background:url("../img/ko_KR/btn_set.png?130306") -41px 0 no-repeat}
|
||||
/* TEXT_TOOLBAR : QUOTE */
|
||||
#smart_editor2 .se2_quote{width:425px;height:56px}
|
||||
#smart_editor2 .se2_quote ul{_display:inline;float:left;margin:11px 0 0 9px;padding:0}
|
||||
#smart_editor2 .se2_quote li{_display:inline;float:left;margin:0 0 0 2px;padding:0}
|
||||
#smart_editor2 .se2_quote button{width:34px;height:34px;margin:0;padding:0;background:url("../img/ko_KR/btn_set.png?130306") no-repeat;cursor:pointer}
|
||||
#smart_editor2 .se2_quote button span{left:0;display:block;visibility:visible;overflow:visible;width:32px;height:32px;margin:0;padding:0;border:1px solid #c7c7c7}
|
||||
#smart_editor2 .se2_quote button span span{visibility:hidden;overflow:hidden;position:absolute;top:0;left:0;width:0;height:0;margin:0;padding:0}
|
||||
#smart_editor2 .se2_quote .se2_quote1{background-position:1px -375px}
|
||||
#smart_editor2 .se2_quote .se2_quote2{background-position:-32px -375px}
|
||||
#smart_editor2 .se2_quote .se2_quote3{background-position:-65px -375px}
|
||||
#smart_editor2 .se2_quote .se2_quote4{background-position:-98px -375px}
|
||||
#smart_editor2 .se2_quote .se2_quote5{background-position:-131px -375px}
|
||||
#smart_editor2 .se2_quote .se2_quote6{background-position:-164px -375px}
|
||||
#smart_editor2 .se2_quote .se2_quote7{background-position:-197px -375px}
|
||||
#smart_editor2 .se2_quote .se2_quote8{background-position:-230px -375px}
|
||||
#smart_editor2 .se2_quote .se2_quote9{background-position:-263px -375px}
|
||||
#smart_editor2 .se2_quote .se2_quote10{background-position:-296px -375px}
|
||||
#smart_editor2 .se2_quote .hover button span,#smart_editor2 .se2_quote .active button span{width:30px;height:30px;margin:0;padding:0;border:2px solid #44b525}
|
||||
#smart_editor2 .se2_quote .hover button span span,#smart_editor2 .se2_quote .active button span span{visibility:hidden;overflow:hidden;position:absolute;top:0;left:0;width:0;height:0;margin:0;padding:0}
|
||||
#smart_editor2 .se2_quote .se2_cancel2{float:left;width:40px;height:35px;margin:11px 0 0 5px;background:url("../img/ko_KR/btn_set.png?130306") -46px -24px no-repeat}
|
||||
#smart_editor2 .se2_quote .se2_cancel2 span{visibility:hidden;overflow:hidden;position:absolute;top:0;left:0;width:0;height:0;margin:0;padding:0}
|
||||
/* TEXT_TOOLBAR : HYPERLINK */
|
||||
#smart_editor2 .se2_url2{width:281px;padding:11px 11px 6px 11px;color:#666}
|
||||
#smart_editor2 .se2_url2 .input_ty1{display:block;width:185px;height:16px;margin:0 5px 5px 0;*margin:-1px 5px 5px 0;padding:5px 2px 0 4px}
|
||||
#smart_editor2 .se2_url2 .se2_url_new{width:15px;height:15px;margin:-1px 3px 1px -1px;*margin:-2px 3px 2px -1px;vertical-align:middle}
|
||||
#smart_editor2 .se2_url2 label{font-size:11px;line-height:14px;vertical-align:middle}
|
||||
#smart_editor2 .se2_url2 .se2_apply{position:absolute;top:13px;right:51px;width:41px;height:24px;margin:-1px 3px 1px 0;background:url("../img/ko_KR/btn_set.png?130306") no-repeat}
|
||||
#smart_editor2 .se2_url2 .se2_cancel{position:absolute;top:13px;right:9px;width:39px;height:24px;margin:-1px 3px 1px 0;background:url("../img/ko_KR/btn_set.png?130306") -41px 0 no-repeat}
|
||||
/* TEXT_TOOLBAR : SCHARACTER */
|
||||
#smart_editor2 .se2_bx_character{width:469px;height:272px;margin:0;padding:0;background:url("../img/ko_KR/bx_set_110302.gif") 9px -1230px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .se2_char_tab{_display:inline;float:left;position:relative;width:443px;margin:11px 10px 200px 11px;padding:0 0 0 1px}
|
||||
#smart_editor2 .se2_bx_character .se2_char_tab li{position:static;margin:0 0 0 -1px;padding:0}
|
||||
#smart_editor2 .se2_bx_character .se2_char1{width:76px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") 0 -204px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .se2_char2{width:86px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -75px -204px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .se2_char3{width:68px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -160px -204px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .se2_char4{width:55px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -227px -204px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .se2_char5{width:97px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -281px -204px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .se2_char6{width:66px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -377px -204px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .active .se2_char1{width:76px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") 0 -230px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .active .se2_char2{width:86px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -75px -230px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .active .se2_char3{width:68px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -160px -230px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .active .se2_char4{width:55px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -227px -230px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .active .se2_char5{width:97px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -281px -230px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .active .se2_char6{width:66px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -377px -230px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .se2_s_character{display:none;position:absolute;top:26px;left:0;width:448px;height:194px;margin:0;padding:0}
|
||||
#smart_editor2 .se2_bx_character .active .se2_s_character{display:block}
|
||||
#smart_editor2 .se2_bx_character .se2_s_character ul{float:left;width:422px;height:172px;margin:0;padding:9px 0 0 11px}
|
||||
#smart_editor2 .se2_bx_character .se2_s_character li{_display:inline;float:left;position:relative;width:20px;height:18px;margin:0 0 1px 1px;background:#fff}
|
||||
#smart_editor2 .se2_bx_character .se2_s_character button{width:20px;height:18px;margin:0;padding:2px;background:none}
|
||||
#smart_editor2 .se2_bx_character .se2_s_character .hover,#smart_editor2 .se2_bx_character .se2_s_character .active{background:url("../img/ko_KR/btn_set.png?130306") -446px -274px no-repeat}
|
||||
#smart_editor2 .se2_bx_character .se2_s_character button span{left:0;display:block;visibility:visible;overflow:visible;width:14px;height:16px;margin:3px 0 0 3px;border:0;background:none;font-size:12px;line-height:normal}
|
||||
#smart_editor2 .se2_apply_character{clear:both;position:relative;padding:0 0 0 11px}
|
||||
#smart_editor2 .se2_apply_character label{margin:0 3px 0 0;font-size:12px;color:#666;letter-spacing:-1px}
|
||||
#smart_editor2 .se2_apply_character .input_ty1{width:283px;height:17px;margin:-1px 5px 1px 0;padding:4px 0 0 5px;font-size:12px;color:#666;letter-spacing:0;vertical-align:middle}
|
||||
#smart_editor2 .se2_apply_character .se2_confirm{width:41px;height:24px;margin-right:3px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat;vertical-align:middle}
|
||||
#smart_editor2 .se2_apply_character .se2_cancel{width:39px;height:24px;background:url("../img/ko_KR/btn_set.png?130306") -41px 0 no-repeat;vertical-align:middle}
|
||||
/* TEXT_TOOLBAR : TABLECREATOR */
|
||||
#smart_editor2 .se2_table_set{position:relative;width:166px;margin:3px 11px 0 11px;padding:8px 0 0 0}
|
||||
#smart_editor2 .se2_table_set .se2_cell_num{float:left;width:73px}
|
||||
#smart_editor2 .se2_table_set .se2_cell_num dt{float:left;clear:both;width:17px;height:23px;margin:0;padding:0}
|
||||
#smart_editor2 .se2_table_set .se2_cell_num dt label{display:block;margin:5px 0 0 0;font-size:11px;color:#666}
|
||||
#smart_editor2 .se2_table_set .se2_cell_num dd{float:left;position:relative;width:54px;height:23px;margin:0;padding:0}
|
||||
#smart_editor2 .se2_table_set .se2_cell_num .input_ty2{display:block;width:32px;height:16px;*margin:-1px 0 0 0;padding:2px 19px 0 0px;border:1px solid #c7c7c7;font-family:tahoma,verdana,times New Roman;font-size:11px;color:#666;text-align:right;*direction:rtl}
|
||||
#smart_editor2 .se2_table_set .se2_cell_num .input_ty2::-ms-clear{display:none}
|
||||
#smart_editor2 .se2_table_set .se2_pre_table{float:right;width:91px;height:43px;background:#c7c7c7;border-spacing:1px}
|
||||
#smart_editor2 .se2_table_set .se2_pre_table tr{background:#fff}
|
||||
#smart_editor2 .se2_table_set .se2_pre_table td{font-size:0;line-height:0}
|
||||
#smart_editor2 .se2_table_set .se2_add{position:absolute;top:2px;right:3px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -54px no-repeat}
|
||||
#smart_editor2 .se2_table_set .se2_del{position:absolute;top:10px;right:3px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -62px no-repeat}
|
||||
/* TEXT_TOOLBAR : TABLEEDITOR */
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1{float:left;width:166px;margin:7px 0 0 0;padding:10px 0 5px;background:url("../img/bg_line1.gif") repeat-x}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1 dt{width:166px;margin:0 0 6px 0}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1 dd{width:166px}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1 dt input{width:15px;height:15px;margin:-1px 3px 1px 0;_margin:-2px 3px 2px 0;vertical-align:middle}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1 dt label{font-weight:bold;font-size:11px;color:#666;letter-spacing:-1px;vertical-align:middle}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_1{float:left;position:relative;z-index:59;width:166px;margin:1px 0 0 0}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_2{z-index:54;margin:0}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_3{z-index:53;margin:0}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_4{z-index:52;margin:0}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 dt{_display:inline;float:left;clear:both;width:66px;height:22px;margin:1px 0 0 18px}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 dt label{display:block;margin:4px 0 0 0;font-weight:normal;font-size:11px;color:#666;letter-spacing:-1px}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 dd{float:left;position:relative;width:82px;height:23px}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 .input_ty1{width:72px;height:16px;*margin:-1px 0 0 0;padding:2px 2px 0 6px;font-family:tahoma,verdana,times New Roman;font-size:11px;color:#666}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 .input_ty3{float:left;width:49px;height:16px;margin:0 3px 0 0;padding:2px 4px 0 4px;border:1px solid #c7c7c7;font-family:tahoma,verdana,times New Roman;font-size:11px;color:#666}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 .se2_add{top:2px;right:2px}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 .se2_del{top:10px;right:2px}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 .se2_color_set .input_ty1{_display:inline;float:left;width:67px;height:16px;margin:0 3px 0 3px;padding:2px 2px 0 4px;font-family:tahoma,verdana,times New Roman;font-size:11px}
|
||||
#smart_editor2 .se2_select_ty1{position:relative;width:80px;height:18px;border:1px solid #c7c7c7;background:#fff;font-size:11px;line-height:14px;text-align:left}
|
||||
#smart_editor2 .se2_select_ty1 span{float:left;width:54px;height:18px;margin:0 0 0 5px;font-size:11px;line-height:14px;color:#666}
|
||||
#smart_editor2 .se2_select_ty1 .se2_b_style0{position:relative;top:3px;left:-3px;white-space:nowrap}
|
||||
#smart_editor2 .se2_select_ty1 .se2_b_style1{height:15px;margin:3px 0 0 4px;font-size:11px;line-height:14px;color:#666;letter-spacing:-1px}
|
||||
#smart_editor2 .se2_select_ty1 .se2_b_style2{background:url("../img/bg_set.gif") 0 -50px repeat-x}
|
||||
#smart_editor2 .se2_select_ty1 .se2_b_style3{background:url("../img/bg_set.gif") 0 -68px repeat-x}
|
||||
#smart_editor2 .se2_select_ty1 .se2_b_style4{background:url("../img/bg_set.gif") 0 -85px repeat-x}
|
||||
#smart_editor2 .se2_select_ty1 .se2_b_style5{background:url("../img/bg_set.gif") 0 -103px repeat-x}
|
||||
#smart_editor2 .se2_select_ty1 .se2_b_style6{background:url("../img/bg_set.gif") 0 -121px repeat-x}
|
||||
#smart_editor2 .se2_select_ty1 .se2_b_style7{background:url("../img/bg_set.gif") 0 -139px repeat-x}
|
||||
#smart_editor2 .se2_select_ty1 .se2_view_more{position:absolute;top:1px;right:1px;width:13px;height:16px;background:url("../img/ko_KR/btn_set.png?130306") -112px -54px no-repeat}
|
||||
#smart_editor2 .se2_select_ty1 .se2_view_more2{position:absolute;top:1px;right:1px;width:13px;height:16px;background:url("../img/ko_KR/btn_set.png?130306") -99px -54px no-repeat}
|
||||
/* TEXT_TOOLBAR : TABLEEDITOR > BORDER */
|
||||
#smart_editor2 .se2_table_set .se2_b_t_b1{border-top:1px solid #b1b1b1}
|
||||
#smart_editor2 .se2_layer_b_style{position:absolute;top:20px;right:0px;width:80px;padding-bottom:1px;border:1px solid #c7c7c7;border-top:1px solid #a8a8a8;background:#fff}
|
||||
#smart_editor2 .se2_layer_b_style ul{width:80px;margin:0;padding:1px 0 0 0}
|
||||
#smart_editor2 .se2_layer_b_style li{width:80px;height:18px;margin:0;padding:0}
|
||||
#smart_editor2 .se2_layer_b_style .hover,#smart_editor2 .se2_layer_b_style .active{background:#ebebeb}
|
||||
#smart_editor2 .se2_layer_b_style button{width:80px;height:18px;background:none}
|
||||
#smart_editor2 .se2_layer_b_style button span{left:0;display:block;visibility:visible;overflow:visible;width:71px;height:18px;margin:0 0 0 5px;font-size:11px;line-height:15px;text-align:left}
|
||||
#smart_editor2 .se2_layer_b_style button span span{visibility:hidden;overflow:hidden;position:absolute;top:0;left:0;width:0;height:0}
|
||||
#smart_editor2 .se2_layer_b_style .se2_b_style1 span{margin:3px 0 0 4px;font-size:11px;line-height:14px;color:#666;letter-spacing:-1px}
|
||||
#smart_editor2 .se2_layer_b_style .se2_b_style2 span{background:url("../img/bg_set.gif") 0 -50px repeat-x}
|
||||
#smart_editor2 .se2_layer_b_style .se2_b_style3 span{background:url("../img/bg_set.gif") 0 -68px repeat-x}
|
||||
#smart_editor2 .se2_layer_b_style .se2_b_style4 span{background:url("../img/bg_set.gif") 0 -86px repeat-x}
|
||||
#smart_editor2 .se2_layer_b_style .se2_b_style5 span{background:url("../img/bg_set.gif") 0 -103px repeat-x}
|
||||
#smart_editor2 .se2_layer_b_style .se2_b_style6 span{background:url("../img/bg_set.gif") 0 -121px repeat-x}
|
||||
#smart_editor2 .se2_layer_b_style .se2_b_style7 span{background:url("../img/bg_set.gif") 0 -139px repeat-x}
|
||||
/* TEXT_TOOLBAR : TABLEEDITOR > COLOR */
|
||||
#smart_editor2 .se2_pre_color{float:left;width:18px;height:18px;border:1px solid #c7c7c7}
|
||||
#smart_editor2 .se2_pre_color button{float:left;width:14px;height:14px;margin:2px 0 0 2px;padding:0}
|
||||
#smart_editor2 .se2_pre_color button span{overflow:hidden;position:absolute;top:-10000px;left:-10000px;z-index:-100;width:0;height:0}
|
||||
/* TEXT_TOOLBAR : TABLEEDITOR > DIMMED */
|
||||
#smart_editor2 .se2_table_set .se2_t_dim1{clear:both;position:absolute;top:71px;left:16px;z-index:60;width:157px;height:118px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_table_set .se2_t_dim2{position:absolute;top:116px;left:16px;z-index:55;width:157px;height:45px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_table_set .se2_t_dim3{clear:both;position:absolute;top:192px;left:16px;z-index:51;width:157px;height:39px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
/* TEXT_TOOLBAR : TABLEEDITOR > STYLE PREVIEW */
|
||||
#smart_editor2 .se2_table_set .se2_t_proper2{float:left;position:relative;z-index:50;width:166px;margin:2px 0 0 0}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper2 dt{float:left;width:84px;height:33px;margin:4px 0 0 0}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper2 dt input{width:15px;height:15px;margin:-1px 3px 1px 0;_margin:-2px 3px 2px 0;vertical-align:middle}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper2 dt label{font-weight:bold;font-size:11px;color:#666;letter-spacing:-1px;vertical-align:middle}
|
||||
#smart_editor2 .se2_table_set .se2_t_proper2 dd{float:left;width:66px;height:33px}
|
||||
#smart_editor2 .se2_select_ty2{position:relative;width:65px;height:31px;border:1px solid #c7c7c7;background:#fff;font-size:11px;line-height:14px;text-align:left}
|
||||
#smart_editor2 .se2_select_ty2 span{float:left;width:45px;height:25px;margin:3px 0 0 3px;background:url("../img/ko_KR/btn_set.png?130306") repeat-x}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style1{background-position:0 -410px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style2{background-position:-46px -410px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style3{background-position:-92px -410px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style4{background-position:-138px -410px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style5{background-position:-184px -410px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style6{background-position:-230px -410px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style7{background-position:-276px -410px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style8{background-position:-322px -410px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style9{background-position:0 -436px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style10{background-position:-46px -436px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style11{background-position:-92px -436px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style12{background-position:-138px -436px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style13{background-position:-184px -436px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style14{background-position:-230px -436px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style15{background-position:-276px -436px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_t_style16{background-position:-322px -436px}
|
||||
#smart_editor2 .se2_select_ty2 .se2_view_more{position:absolute;top:1px;right:1px;_right:0px;width:13px !important;height:29px !important;background:url("../img/ko_KR/btn_set.png?130306") -353px -48px no-repeat !important}
|
||||
#smart_editor2 .se2_select_ty2 .se2_view_more2{position:absolute;top:1px;right:1px;_right:0px;width:13px !important;height:29px !important;background:url("../img/ko_KR/btn_set.png?130306") -340px -48px no-repeat !important}
|
||||
#smart_editor2 .se2_select_ty2 .se2_view_more span{display:none}
|
||||
/* TEXT_TOOLBAR : TABLEEDITOR > STYLE */
|
||||
#smart_editor2 .se2_layer_t_style{position:absolute;top:33px;right:15px;width:208px;border:1px solid #c7c7c7;border-top:1px solid #a8a8a8;background:#fff}
|
||||
#smart_editor2 .se2_layer_t_style ul{width:204px;height:126px;margin:1px 2px;padding:1px 0 0 0;background:#fff}
|
||||
#smart_editor2 .se2_layer_t_style li{_display:inline;float:left;width:45px;height:25px;margin:1px;padding:1px;border:1px solid #fff}
|
||||
#smart_editor2 .se2_layer_t_style .hover,#smart_editor2 .se2_layer_t_style .active{border:1px solid #666;background:#fff}
|
||||
#smart_editor2 .se2_layer_t_style button{width:45px;height:25px;background:url("../img/ko_KR/btn_set.png?130306") repeat-x !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style1{background-position:0 -410px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style2{background-position:-46px -410px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style3{background-position:-92px -410px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style4{background-position:-138px -410px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style5{background-position:-184px -410px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style6{background-position:-230px -410px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style7{background-position:-276px -410px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style8{background-position:-322px -410px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style9{background-position:0 -436px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style10{background-position:-46px -436px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style11{background-position:-92px -436px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style12{background-position:-138px -436px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style13{background-position:-184px -436px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style14{background-position:-230px -436px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style15{background-position:-276px -436px !important}
|
||||
#smart_editor2 .se2_layer_t_style .se2_t_style16{background-position:-322px -436px !important}
|
||||
#smart_editor2 .se2_table_set .se2_btn_area{float:left;width:166px;margin:6px 0 0 0;padding:12px 0 8px 0;background:url("../img/bg_line1.gif") repeat-x;text-align:center}
|
||||
#smart_editor2 .se2_table_set button.se2_apply{width:41px;height:24px;margin-right:3px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat}
|
||||
#smart_editor2 .se2_table_set button.se2_cancel{width:39px;height:24px;background:url("../img/ko_KR/btn_set.png?130306") -41px 0 no-repeat}
|
||||
#smart_editor2 .se2_table_set .se2_rd{width:14px;height:14px;vertical-align:middle}
|
||||
#smart_editor2 .se2_table_set .se2_celltit{font-size:11px;font-size:11px;color:#666;letter-spacing:-1px}
|
||||
#smart_editor2 .se2_table_set dt label.se2_celltit{display:inline}
|
||||
/* TEXT_TOOLBAR : FINDREPLACE */
|
||||
#smart_editor2 .se2_bx_find_revise{position:relative;width:255px;margin:0;padding:0}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_close{position:absolute;top:5px;right:8px;width:20px;height:20px;background:url("../img/ko_KR/btn_set.png?130306") -151px -1px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise h3{margin:0;padding:10px 0 13px 10px;background:url("../img/bg_find_h3.gif") 0 -1px repeat-x;font-size:12px;line-height:14px;letter-spacing:-1px}
|
||||
#smart_editor2 .se2_bx_find_revise ul{position:relative;margin:8px 0 0 0;padding:0 0 0 12px}
|
||||
#smart_editor2 .se2_bx_find_revise ul li{_display:inline;float:left;position:static;margin:0 0 0 -1px;padding:0}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_tabfind{width:117px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") 0 -100px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_tabrevise{width:117px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -116px -100px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise .active .se2_tabfind{width:117px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") 0 -126px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise .active .se2_tabrevise{width:117px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -116px -126px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_in_bx_find dl{_display:inline;float:left;width:223px;margin:0 0 0 9px;padding:7px 0 13px 14px;background:url("../img/ko_KR/bx_set_110302.gif") -289px -1518px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_in_bx_revise dl{_display:inline;float:left;width:223px;margin:0 0 0 9px;padding:7px 0 13px 14px;background:url("../img/ko_KR/bx_set_110302.gif") -289px -1619px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise dt{_display:inline;float:left;clear:both;width:47px;margin:1px 0 2px 0}
|
||||
#smart_editor2 .se2_bx_find_revise dd{float:left;margin:0 0 2px 0}
|
||||
#smart_editor2 .se2_bx_find_revise label{float:left;padding:5px 0 0 0;font-size:11px;color:#666;letter-spacing:-2px}
|
||||
#smart_editor2 .se2_bx_find_revise input{float:left;width:155px;height:12px;margin:1px 0 0 0;padding:3px 2px 3px 4px;font-size:12px;color:#666}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_find_btns{float:left;clear:both;width:255px;padding:8px 0 10px 0;text-align:center}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_find_next{width:65px;height:24px;margin:0 3px 0 0;background:url("../img/ko_KR/btn_set.png?130306") -180px -48px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_find_next2{width:61px;height:24px;margin:0 3px 0 0;background:url("../img/ko_KR/btn_set.png?130306") -180px -24px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_revise1{width:54px;height:24px;margin:0 3px 0 0;background:url("../img/ko_KR/btn_set.png?130306") -245px -48px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_revise2{width:70px;height:24px;margin:0 3px 0 0;background:url("../img/ko_KR/btn_set.png?130306") -245px -24px no-repeat}
|
||||
#smart_editor2 .se2_bx_find_revise .se2_cancel{width:39px;height:24px;background:url("../img/ko_KR/btn_set.png?130306") -41px 0 no-repeat}
|
||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE */
|
||||
#smart_editor2 .se2_qmax{position:absolute;width:18px;height:18px;background:url("../img/ko_KR/btn_set.png?130306") -339px -169px no-repeat}
|
||||
#smart_editor2 .se2_qeditor{position:absolute;top:0;left:0;width:183px;margin:0;padding:0;border:1px solid #c7c7c7;border-right:1px solid #ababab;border-bottom:1px solid #ababab;background:#fafafa}
|
||||
#smart_editor2 .se2_qeditor label,#smart_editor2 .se2_qeditor span,#smart_editor2 .se2_qeditor dt{font-size:11px;color:#666;letter-spacing:-1px}
|
||||
#smart_editor2 .se2_qbar{position:relative;width:183px;height:11px;background:url("../img/ko_KR/bx_set_110302.gif") 0 -731px no-repeat}
|
||||
#smart_editor2 .se2_qbar .se2_qmini{position:absolute;top:-1px;right:0;*right:-1px;_right:-3px;width:18px;height:14px;background:url("../img/ko_KR/btn_set.png?130306") -315px -170px no-repeat}
|
||||
#smart_editor2 .se2_qbar .se2_qmini button{width:20px;height:14px;margin-top:-1px}
|
||||
#smart_editor2 .se2_qeditor .se2_qbody0{float:left;border:1px solid #fefefe}
|
||||
#smart_editor2 .se2_qeditor .se2_qbody{position:relative;z-index:90;width:174px;padding:4px 0 0 7px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe1{overflow:hidden;width:174px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe1 dt{float:left;width:22px;height:18px;padding:4px 0 0 0}
|
||||
#smart_editor2 .se2_qeditor .se2_qe1 dd{float:left;width:65px;height:22px}
|
||||
#smart_editor2 .se2_qeditor .se2_addrow{width:28px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -49px}
|
||||
#smart_editor2 .se2_qeditor .se2_addcol{width:29px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -49px}
|
||||
#smart_editor2 .se2_qeditor .se2_seprow{width:28px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -68px}
|
||||
#smart_editor2 .se2_qeditor .se2_sepcol{width:29px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -68px}
|
||||
#smart_editor2 .se2_qeditor .se2_delrow{width:28px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -106px}
|
||||
#smart_editor2 .se2_qeditor .se2_delcol{width:29px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -106px}
|
||||
#smart_editor2 .se2_qeditor .se2_merrow{width:57px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -125px}
|
||||
#smart_editor2 .se2_qeditor .se2_mercol{width:57px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -125px}
|
||||
#smart_editor2 .se2_qeditor .se2_seprow_off{width:28px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -87px}
|
||||
#smart_editor2 .se2_qeditor .se2_sepcol_off{width:29px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -87px}
|
||||
#smart_editor2 .se2_qeditor .se2_merrow_off{width:57px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -144px}
|
||||
#smart_editor2 .se2_qeditor .se2_mercol_off{width:57px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -144px}
|
||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > CELL_BACKGROUND */
|
||||
#smart_editor2 .se2_qeditor .se2_qe2{_display:inline;float:left;position:relative;z-index:100;width:165px;margin:2px 0 0 1px;padding:7px 0 0 0;background:url("../img/bg_line1.gif") repeat-x;zoom:1}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_1 dt{float:left;width:62px;padding:3px 0 0 0}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_1 dt input{width:15px;height:15px;margin:-1px 1px 1px -1px;vertical-align:middle}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_1 dd{float:left;position:relative;zoom:1}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_3{padding:7px 0 6px 0}
|
||||
/* My글양식 없을때 */
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2{position:relative;_position:absolute}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 dt{float:left;width:50px;padding:3px 0 0 13px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 dt input{width:15px;height:15px;margin:-1px 2px 1px -1px;vertical-align:middle}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 dd{float:left}
|
||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > STYLE */
|
||||
#smart_editor2 .se2_table_set .se2_qbody .se2_t_proper2{float:left;*float:none;position:static;width:166px;margin:5px 0 0 1px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe3 dt{float:left;width:62px;padding:0}
|
||||
#smart_editor2 .se2_qeditor .se2_qe3 dt label{font-weight:normal}
|
||||
#smart_editor2 .se2_qeditor .se2_qe3 dt input{width:15px;height:15px;margin:-1px 1px 1px -1px;vertical-align:middle}
|
||||
#smart_editor2 .se2_qeditor .se2_qe3 dd .se2_qe3_table{position:relative}
|
||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > CELL_BACKGROUND PREWVIEW */
|
||||
#smart_editor2 .se2_qeditor .se2_pre_color{float:left;width:18px;height:18px;border:1px solid #c7c7c7}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_color button{float:left;width:14px;height:14px;margin:2px 0 0 2px;padding:0}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_color button span{overflow:hidden;position:absolute;top:-10000px;left:-10000px;z-index:-100;width:0;height:0}
|
||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > CELL_BACKGROUND LAYER */
|
||||
#smart_editor2 .se2_qeditor .se2_layer{float:left;clear:both;position:absolute;top:20px;left:0;margin:0;padding:0;border:1px solid #c7c7c7;border-top:1px solid #9a9a9a;background:#fafafa}
|
||||
#smart_editor2 .se2_qeditor .se2_layer .se2_in_layer{float:left;margin:0;padding:0;border:1px solid #fff;background:#fafafa}
|
||||
#smart_editor2 .se2_qeditor .se2_layer button{vertical-align:top}
|
||||
#smart_editor2 .se2_qeditor .se2_layer .se2_pick_color li{position:relative}
|
||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > CELL_BACKGROUND IMAGE */
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg{float:left;width:14px;height:14px;padding:2px;border:1px solid #c7c7c7}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 button{width:16px;height:16px;background:url("../img/ko_KR/btn_set.png?130306") 0 -261px no-repeat}
|
||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > CELL_BACKGROUND IMAGE LAYER */
|
||||
#smart_editor2 .se2_cellimg_set{_display:inline;float:left;width:136px;margin:4px 3px 0 4px;padding-bottom:4px}
|
||||
#smart_editor2 .se2_cellimg_set li{_display:inline;float:left;width:16px;height:16px;margin:0 1px 1px 0}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg0{background-position:-255px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg1{background-position:0 -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg2{background-position:-17px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg3{background-position:-34px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg4{background-position:-51px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg5{background-position:-68px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg6{background-position:-85px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg7{background-position:-102px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg8{background-position:-119px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg9{background-position:-136px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg10{background-position:-153px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg11{background-position:-170px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg12{background-position:-187px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg13{background-position:-204px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg14{background-position:-221px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg15{background-position:-238px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg16{background-position:-255px -261px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg17{background-position:0 -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg18{background-position:-17px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg19{background-position:-34px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg20{background-position:-51px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg21{background-position:-68px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg22{background-position:-85px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg23{background-position:-102px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg24{background-position:-119px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg25{background-position:-136px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg26{background-position:-153px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg27{background-position:-170px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg28{background-position:-187px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg29{background-position:-204px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg30{background-position:-221px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg31{background-position:-238px -278px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg button{width:14px;height:14px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg1{background-position:-1px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg2{background-position:-18px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg3{background-position:-35px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg4{background-position:-52px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg5{background-position:-69px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg6{background-position:-86px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg7{background-position:-103px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg8{background-position:-120px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg9{background-position:-137px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg10{background-position:-154px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg11{background-position:-171px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg12{background-position:-188px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg13{background-position:-205px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg14{background-position:-222px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg15{background-position:-239px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg16{background-position:-256px -262px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg17{background-position:-1px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg18{background-position:-18px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg19{background-position:-35px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg20{background-position:-52px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg21{background-position:-69px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg22{background-position:-86px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg23{background-position:-103px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg24{background-position:-120px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg25{background-position:-137px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg26{background-position:-154px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg27{background-position:-171px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg28{background-position:-188px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg29{background-position:-205px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg30{background-position:-222px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg31{background-position:-239px -279px}
|
||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg32{background-position:-256px -279px}
|
||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > MY REVIEW */
|
||||
#smart_editor2 .se2_btn_area{_display:inline;float:left;clear:both;width:166px;margin:5px 0 0 1px;padding:7px 0 6px 0;background:url("../img/bg_line1.gif") repeat-x;text-align:center}
|
||||
#smart_editor2 .se2_btn_area .se2_btn_save{width:97px;height:21px;background:url("../img/ko_KR/btn_set.png?130306") -369px -163px no-repeat}
|
||||
/* TEXT_TOOLBAR : QUICKEDITOR_IMAGE */
|
||||
#smart_editor2 .se2_qe10{width:166px;margin:0;*margin:-2px 0 0 0}
|
||||
#smart_editor2 .se2_qe10 label{margin:0 1px 0 0;vertical-align:middle}
|
||||
#smart_editor2 .se2_qe10 .se2_sheight{margin-left:4px}
|
||||
#smart_editor2 .se2_qe10 .input_ty1{width:30px;height:13px;margin:0 0 1px 1px;padding:3px 4px 0 1px;font-size:11px;letter-spacing:0;text-align:right;vertical-align:middle}
|
||||
#smart_editor2 .se2_qe10 .se2_sreset{width:41px;height:19px;margin-left:3px;background:url("../img/ko_KR/btn_set.png?130306") -401px -184px no-repeat;vertical-align:middle}
|
||||
#smart_editor2 .se2_qe10_1{margin-top:4px;padding:10px 0 3px;background:url("../img/bg_line1.gif") repeat-x}
|
||||
#smart_editor2 .se2_qe10_1 input{width:15px;height:15px;margin:-1px 3px 1px -1px;vertical-align:middle}
|
||||
#smart_editor2 .se2_qe11{float:left;width:166px;margin:4px 0 0 0;padding:7px 0 2px 0;background:url("../img/bg_line1.gif") repeat-x}
|
||||
#smart_editor2 .se2_qe11_1{float:left;width:99px}
|
||||
#smart_editor2 .se2_qe11_1 dt{float:left;width:56px;height:15px;padding:5px 0 0 0}
|
||||
#smart_editor2 .se2_qe11_1 dd{float:left;position:relative;width:38px;height:20px}
|
||||
#smart_editor2 .se2_qe11_1 .input_ty1{display:block;width:29px;height:15px;margin:0;*margin:-1px 0 1px 0;padding:3px 1px 0 5px;font-size:11px;letter-spacing:0;text-align:left}
|
||||
#smart_editor2 .se2_qe11_1 .se2_add{position:absolute;top:2px;right:3px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -54px no-repeat}
|
||||
#smart_editor2 .se2_qe11_1 .se2_del{position:absolute;top:10px;right:3px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -62px no-repeat}
|
||||
#smart_editor2 .se2_qe11_2{float:left;width:67px}
|
||||
#smart_editor2 .se2_qe11_2 dt{float:left;width:47px;margin:5px 0 0 0}
|
||||
#smart_editor2 .se2_qe11_2 dd{float:left;position:relative;width:20px}
|
||||
#smart_editor2 .se2_qe12{float:left;width:166px;margin:3px 0 0 0;padding:7px 0 0 0;background:url("../img/bg_line1.gif") repeat-x}
|
||||
#smart_editor2 .se2_qe12 dt{float:left;margin:5px 4px 0 0}
|
||||
#smart_editor2 .se2_qe12 dd{float:left;padding:0 0 6px 0}
|
||||
#smart_editor2 .se2_qe12 .se2_align0{float:left;width:19px;height:21px;background:url("../img/ko_KR/btn_set.png?130306") -276px -121px no-repeat}
|
||||
#smart_editor2 .se2_qe12 .se2_align1{float:left;width:19px;height:21px;background:url("../img/ko_KR/btn_set.png?130306") -295px -121px no-repeat}
|
||||
#smart_editor2 .se2_qe12 .se2_align2{float:left;width:20px;height:21px;background:url("../img/ko_KR/btn_set.png?130306") -314px -121px no-repeat}
|
||||
#smart_editor2 .se2_qe13{position:relative;z-index:10;zoom:1}
|
||||
#smart_editor2 .se2_qe13 dt{float:left;width:62px;padding:3px 0 0}
|
||||
#smart_editor2 .se2_qe13 dt input{width:15px;height:15px;margin:-1px 1px 1px -1px;vertical-align:middle;zoom:1}
|
||||
#smart_editor2 .se2_qe13 dt .se2_qdim2{width:32px}
|
||||
#smart_editor2 .se2_qe13 dd .se2_select_ty1{width:38px}
|
||||
#smart_editor2 .se2_qe13 dd .se2_select_ty1 span{width:15px}
|
||||
#smart_editor2 .se2_qe13 dd .input_ty1{width:20px}
|
||||
#smart_editor2 .se2_qe13 dd .se2_palette2 .input_ty1{width:67px}
|
||||
#smart_editor2 .se2_qe13 .se2_add{*top:3px}
|
||||
#smart_editor2 .se2_qe13 .se2_del{*top:11px}
|
||||
#smart_editor2 .se2_qe13 .se2_layer_b_style{right:-2px;_right:0}
|
||||
#smart_editor2 .se2_qe13 .se2_layer_b_style li span{width:auto;margin:0 4px 0 5px;padding-top:2px}
|
||||
#smart_editor2 .se2_qe13 dd{_display:inline;float:left;position:relative;width:29px;margin-right:5px;_margin-right:3px;zoom:1}
|
||||
#smart_editor2 .se2_qe13 dd .se2_palette h4{margin-top:9px;font-family:dotum;font-size:12px}
|
||||
#smart_editor2 .se2_qe13 dd.dd_type{width:38px}
|
||||
#smart_editor2 .se2_qe13 dd.dd_type2{width:37px;margin-right:3px}
|
||||
#smart_editor2 .se2_qe13 dd.dd_type2 .input_ty1{width:29px}
|
||||
#smart_editor2 .se2_qe13 dd.dd_type2 button{right:2px;_right:1px}
|
||||
#smart_editor2 .se2_qe13 dd.dd_type3{width:20px;margin:0}
|
||||
#smart_editor2 .se2_qe13_v1{_display:inline;float:left;margin:2px 0 1px}
|
||||
#smart_editor2 .se2_qe13_v1 dt{padding:4px 0 0 1px}
|
||||
#smart_editor2 .se2_qe13_v2{_display:inline;float:left;position:relative;z-index:100;width:165px;margin:4px 0 0 1px;zoom:1}
|
||||
#smart_editor2 .se2_qe13_v2 dd{width:18px;margin:0}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim1{clear:both;position:absolute;top:25px;left:115px;width:60px;height:23px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim2{clear:both;position:absolute;top:55px;left:24px;z-index:110;width:70px;height:22px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim3{clear:both;position:absolute;top:55px;left:118px;z-index:110;width:56px;height:22px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim4{clear:both;position:absolute;top:81px;left:23px;z-index:35;width:116px;height:35px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim5{clear:both;position:absolute;top:31px;left:106px;width:68px;height:26px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim6c{clear:both;position:absolute;top:25px;left:28px;width:29px;height:23px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim6r{clear:both;position:absolute;top:25px;left:57px;width:29px;height:23px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_highedit{float:right;width:56px;height:21px;margin:-27px 8px 0 0;background:url("../img/ko_KR/btn_set.png?130306") -329px -142px no-repeat}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim7{clear:both;position:absolute;top:55px;left:24px;z-index:110;width:150px;height:48px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim8{clear:both;position:absolute;top:105px;left:24px;z-index:110;width:150px;height:37px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim9{clear:both;position:absolute;top:55px;left:111px;z-index:110;width:65px;height:24px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim10{clear:both;position:absolute;top:55px;left:100px;z-index:110;width:77px;height:24px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
#smart_editor2 .se2_qeditor .se2_qdim11{clear:both;position:absolute;top:55px;left:65px;z-index:110;width:115px;height:24px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
||||
/* HELP : ACCESSIBILITY */
|
||||
#smart_editor2 .se2_accessibility{z-index:90}
|
||||
#smart_editor2 .se2_accessibility .se2_in_layer{width:568px;padding:0 10px;background:#fafafa;border:1px solid #bcbbbb}
|
||||
#smart_editor2 .se2_accessibility h3{margin:0 -10px;padding:6px 0 12px 0;background:url("../img/bg_find_h3.gif") repeat-x;font-size:12px;line-height:14px;letter-spacing:-1px}
|
||||
#smart_editor2 .se2_accessibility h3 strong{display:inline-block;padding:4px 0 3px 11px;color:#333;letter-spacing:0}
|
||||
#smart_editor2 .se2_accessibility .se2_close{position:absolute;top:10px;right:12px;width:13px;height:12px;background:url("../img/ko_KR/btn_set.png?130306") -155px -5px no-repeat}
|
||||
#smart_editor2 .se2_accessibility .box_help{padding:0 2px;margin-top:8px;background:url("../img/bg_help.gif") 0 100% no-repeat}
|
||||
#smart_editor2 .se2_accessibility .box_help div{overflow:hidden;padding:20px 21px 24px;border-top:1px solid #d0d0d0;color:#333}
|
||||
#smart_editor2 .se2_accessibility .box_help strong{display:block;margin-bottom:2px}
|
||||
#smart_editor2 .se2_accessibility .box_help p{margin-bottom:28px;line-height:1.5}
|
||||
#smart_editor2 .se2_accessibility .box_help ul{width:150%;margin-top:10px}
|
||||
#smart_editor2 .se2_accessibility .box_help li{position:relative;float:left;width:252px;padding:5px 0 5px 9px;margin-right:40px;background:url("../img/ko_KR/btn_set.png?130306") -475px -51px no-repeat;border-right:1px solid #f0f0f0;*zoom:1;line-height:1}
|
||||
#smart_editor2 .se2_accessibility .box_help li span{position:absolute;top:4px;left:138px;line-height:1.2}
|
||||
#smart_editor2 .se2_accessibility .se2_btns{padding:9px 0 10px;text-align:center}
|
||||
#smart_editor2 .se2_accessibility .se2_btns .se2_close2{width:39px;height:24px;background:url("../img/ko_KR/btn_set.png?130306") -235px -120px no-repeat}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
@charset "UTF-8";
|
||||
/* NHN Web Standardization Team (http://html.nhndesign.com/) HHJ 090226 */
|
||||
/* COMMON */
|
||||
.se2_outputarea,.se2_outputarea th,.se2_outputarea td{margin:0;padding:0;color:#666;font-size:12px;font-family:'돋움',Dotum,'굴림',Gulim,Helvetica,Sans-serif;line-height:1.5}
|
||||
.se2_outputarea p{margin:0;padding:0}
|
||||
.se2_outputarea a:hover{text-decoration:underline}
|
||||
.se2_outputarea a:link{color:#0000ff}
|
||||
.se2_outputarea ul{margin:0 0 0 40px;padding:0}
|
||||
.se2_outputarea ul li{margin:0;list-style-type:disc;padding:0}
|
||||
.se2_outputarea ul ul li{list-style-type:circle}
|
||||
.se2_outputarea ul ul ul li{list-style-type:square}
|
||||
.se2_outputarea img,.se2_outputarea fieldset{border:0}
|
||||
|
After Width: | Height: | Size: 115 B |
|
After Width: | Height: | Size: 526 B |
|
After Width: | Height: | Size: 331 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 159 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 103 B |
|
After Width: | Height: | Size: 43 B |
|
After Width: | Height: | Size: 56 B |
|
After Width: | Height: | Size: 941 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 104 B |
|
After Width: | Height: | Size: 139 B |
|
After Width: | Height: | Size: 155 B |
|
After Width: | Height: | Size: 270 B |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
|
@ -0,0 +1,134 @@
|
|||
if(typeof window.nhn=='undefined') window.nhn = {};
|
||||
if (!nhn.husky) nhn.husky = {};
|
||||
|
||||
/**
|
||||
* @fileOverview This file contains application creation helper function, which would load up an HTML(Skin) file and then execute a specified create function.
|
||||
* @name HuskyEZCreator.js
|
||||
*/
|
||||
nhn.husky.EZCreator = new (function(){
|
||||
this.nBlockerCount = 0;
|
||||
|
||||
this.createInIFrame = function(htOptions){
|
||||
if(arguments.length == 1){
|
||||
var oAppRef = htOptions.oAppRef;
|
||||
var elPlaceHolder = htOptions.elPlaceHolder;
|
||||
var sSkinURI = htOptions.sSkinURI;
|
||||
var fCreator = htOptions.fCreator;
|
||||
var fOnAppLoad = htOptions.fOnAppLoad;
|
||||
var bUseBlocker = htOptions.bUseBlocker;
|
||||
var htParams = htOptions.htParams || null;
|
||||
}else{
|
||||
// for backward compatibility only
|
||||
var oAppRef = arguments[0];
|
||||
var elPlaceHolder = arguments[1];
|
||||
var sSkinURI = arguments[2];
|
||||
var fCreator = arguments[3];
|
||||
var fOnAppLoad = arguments[4];
|
||||
var bUseBlocker = arguments[5];
|
||||
var htParams = arguments[6];
|
||||
}
|
||||
|
||||
if(bUseBlocker) nhn.husky.EZCreator.showBlocker();
|
||||
|
||||
var attachEvent = function(elNode, sEvent, fHandler){
|
||||
if(elNode.addEventListener){
|
||||
elNode.addEventListener(sEvent, fHandler, false);
|
||||
}else{
|
||||
elNode.attachEvent("on"+sEvent, fHandler);
|
||||
}
|
||||
}
|
||||
|
||||
if(!elPlaceHolder){
|
||||
alert("Placeholder is required!");
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(elPlaceHolder) != "object")
|
||||
elPlaceHolder = document.getElementById(elPlaceHolder);
|
||||
|
||||
var elIFrame, nEditorWidth, nEditorHeight;
|
||||
|
||||
|
||||
try{
|
||||
elIFrame = document.createElement("<IFRAME frameborder=0 scrolling=no>");
|
||||
}catch(e){
|
||||
elIFrame = document.createElement("IFRAME");
|
||||
elIFrame.setAttribute("frameborder", "0");
|
||||
elIFrame.setAttribute("scrolling", "no");
|
||||
}
|
||||
|
||||
elIFrame.style.width = "1px";
|
||||
elIFrame.style.height = "1px";
|
||||
elPlaceHolder.parentNode.insertBefore(elIFrame, elPlaceHolder.nextSibling);
|
||||
|
||||
attachEvent(elIFrame, "load", function(){
|
||||
fCreator = elIFrame.contentWindow[fCreator] || elIFrame.contentWindow.createSEditor2;
|
||||
|
||||
// top.document.title = ((new Date())-window.STime);
|
||||
// window.STime = new Date();
|
||||
|
||||
try{
|
||||
|
||||
nEditorWidth = elIFrame.contentWindow.document.body.scrollWidth || "500px";
|
||||
nEditorHeight = elIFrame.contentWindow.document.body.scrollHeight + 12;
|
||||
elIFrame.style.width = "100%";
|
||||
elIFrame.style.height = nEditorHeight+ "px";
|
||||
elIFrame.contentWindow.document.body.style.margin = "0";
|
||||
}catch(e){
|
||||
nhn.husky.EZCreator.hideBlocker(true);
|
||||
elIFrame.style.border = "5px solid red";
|
||||
elIFrame.style.width = "500px";
|
||||
elIFrame.style.height = "500px";
|
||||
alert("Failed to access "+sSkinURI);
|
||||
return;
|
||||
}
|
||||
|
||||
var oApp = fCreator(elPlaceHolder, htParams); // oEditor
|
||||
|
||||
|
||||
oApp.elPlaceHolder = elPlaceHolder;
|
||||
|
||||
oAppRef[oAppRef.length] = oApp;
|
||||
if(!oAppRef.getById) oAppRef.getById = {};
|
||||
|
||||
if(elPlaceHolder.id) oAppRef.getById[elPlaceHolder.id] = oApp;
|
||||
|
||||
oApp.run({fnOnAppReady:fOnAppLoad});
|
||||
|
||||
// top.document.title += ", "+((new Date())-window.STime);
|
||||
nhn.husky.EZCreator.hideBlocker();
|
||||
});
|
||||
// window.STime = new Date();
|
||||
elIFrame.src = sSkinURI;
|
||||
this.elIFrame = elIFrame;
|
||||
};
|
||||
|
||||
this.showBlocker = function(){
|
||||
if(this.nBlockerCount<1){
|
||||
var elBlocker = document.createElement("DIV");
|
||||
elBlocker.style.position = "absolute";
|
||||
elBlocker.style.top = 0;
|
||||
elBlocker.style.left = 0;
|
||||
elBlocker.style.backgroundColor = "#FFFFFF";
|
||||
elBlocker.style.width = "100%";
|
||||
|
||||
document.body.appendChild(elBlocker);
|
||||
|
||||
nhn.husky.EZCreator.elBlocker = elBlocker;
|
||||
}
|
||||
|
||||
nhn.husky.EZCreator.elBlocker.style.height = Math.max(document.body.scrollHeight, document.body.clientHeight)+"px";
|
||||
|
||||
this.nBlockerCount++;
|
||||
};
|
||||
|
||||
this.hideBlocker = function(bForce){
|
||||
if(!bForce){
|
||||
if(--this.nBlockerCount > 0) return;
|
||||
}
|
||||
|
||||
this.nBlockerCount = 0;
|
||||
|
||||
if(nhn.husky.EZCreator.elBlocker) nhn.husky.EZCreator.elBlocker.style.display = "none";
|
||||
}
|
||||
})();
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
function createSEditor2(elIRField, htParams, elSeAppContainer){
|
||||
if(!window.$Jindo){
|
||||
parent.document.body.innerHTML="진도 프레임웍이 필요합니다.<br>\n<a href='http://dev.naver.com/projects/jindo/download'>http://dev.naver.com/projects/jindo/download</a>에서 Jindo 1.5.3 버전의 jindo.min.js를 다운로드 받아 /js 폴더에 복사 해 주세요.\n(아직 Jindo 2 는 지원하지 않습니다.)";
|
||||
return;
|
||||
}
|
||||
|
||||
var elAppContainer = (elSeAppContainer || jindo.$("smart_editor2"));
|
||||
var elEditingArea = jindo.$$.getSingle("DIV.husky_seditor_editing_area_container", elAppContainer);
|
||||
var oWYSIWYGIFrame = jindo.$$.getSingle("IFRAME.se2_input_wysiwyg", elEditingArea);
|
||||
var oIRTextarea = elIRField?elIRField:jindo.$$.getSingle("TEXTAREA.blind", elEditingArea);
|
||||
var oHTMLSrc = jindo.$$.getSingle("TEXTAREA.se2_input_htmlsrc", elEditingArea);
|
||||
var oTextArea = jindo.$$.getSingle("TEXTAREA.se2_input_text", elEditingArea);
|
||||
|
||||
if(!htParams){
|
||||
htParams = {};
|
||||
htParams.fOnBeforeUnload = null;
|
||||
}
|
||||
htParams.elAppContainer = elAppContainer; // 에디터 UI 최상위 element 셋팅
|
||||
htParams.oNavigator = jindo.$Agent().navigator(); // navigator 객체 셋팅
|
||||
|
||||
var oEditor = new nhn.husky.HuskyCore(htParams);
|
||||
oEditor.registerPlugin(new nhn.husky.CorePlugin(htParams?htParams.fOnAppLoad:null));
|
||||
oEditor.registerPlugin(new nhn.husky.StringConverterManager());
|
||||
|
||||
var htDimension = {
|
||||
nMinHeight:205,
|
||||
nMinWidth:parseInt(elIRField.style.minWidth, 10)||570,
|
||||
nHeight:elIRField.style.height||elIRField.offsetHeight,
|
||||
nWidth:elIRField.style.width||elIRField.offsetWidth
|
||||
};
|
||||
|
||||
var htConversionMode = {
|
||||
bUseVerticalResizer : htParams.bUseVerticalResizer,
|
||||
bUseModeChanger : htParams.bUseModeChanger
|
||||
};
|
||||
|
||||
var aAdditionalFontList = htParams.aAdditionalFontList;
|
||||
|
||||
oEditor.registerPlugin(new nhn.husky.SE_EditingAreaManager("WYSIWYG", oIRTextarea, htDimension, htParams.fOnBeforeUnload, elAppContainer));
|
||||
oEditor.registerPlugin(new nhn.husky.SE_EditingArea_WYSIWYG(oWYSIWYGIFrame)); // Tab Editor 모드
|
||||
oEditor.registerPlugin(new nhn.husky.SE_EditingArea_HTMLSrc(oHTMLSrc)); // Tab HTML 모드
|
||||
oEditor.registerPlugin(new nhn.husky.SE_EditingArea_TEXT(oTextArea)); // Tab Text 모드
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_EditingModeChanger(elAppContainer, htConversionMode)); // 모드간 변경(Editor, HTML, Text)
|
||||
oEditor.registerPlugin(new nhn.husky.SE_PasteHandler()); // WYSIWYG Paste Handler
|
||||
|
||||
oEditor.registerPlugin(new nhn.husky.HuskyRangeManager(oWYSIWYGIFrame));
|
||||
oEditor.registerPlugin(new nhn.husky.Utils());
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_UtilPlugin());
|
||||
oEditor.registerPlugin(new nhn.husky.SE_WYSIWYGStyler());
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_Toolbar(elAppContainer));
|
||||
|
||||
oEditor.registerPlugin(new nhn.husky.Hotkey()); // 단축키
|
||||
oEditor.registerPlugin(new nhn.husky.SE_EditingAreaVerticalResizer(elAppContainer, htConversionMode)); // 편집영역 리사이즈
|
||||
oEditor.registerPlugin(new nhn.husky.DialogLayerManager());
|
||||
oEditor.registerPlugin(new nhn.husky.ActiveLayerManager());
|
||||
oEditor.registerPlugin(new nhn.husky.SE_WYSIWYGStyleGetter()); // 커서 위치 스타일 정보 가져오기
|
||||
|
||||
oEditor.registerPlugin(new nhn.husky.SE_WYSIWYGEnterKey("P")); // 엔터 시 처리, 현재는 P로 처리
|
||||
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_ColorPalette(elAppContainer)); // 색상 팔레트
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_FontColor(elAppContainer)); // 글자색
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_BGColor(elAppContainer)); // 글자배경색
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_FontNameWithLayerUI(elAppContainer, aAdditionalFontList)); // 글꼴종류
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_FontSizeWithLayerUI(elAppContainer)); // 글꼴크기
|
||||
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_LineStyler());
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_ExecCommand(oWYSIWYGIFrame));
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_LineHeightWithLayerUI(elAppContainer)); // 줄간격
|
||||
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_Quote(elAppContainer)); // 인용구
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_Hyperlink(elAppContainer)); // 링크
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_SCharacter(elAppContainer)); // 특수문자
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_FindReplacePlugin(elAppContainer)); // 찾기/바꾸기
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_TableCreator(elAppContainer)); // 테이블 생성
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_TableEditor(elAppContainer)); // 테이블 편집
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_TableBlockStyler(elAppContainer)); // 테이블 스타일
|
||||
if(nhn.husky.SE2M_AttachQuickPhoto){
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_AttachQuickPhoto(elAppContainer)); // 사진
|
||||
}
|
||||
|
||||
oEditor.registerPlugin(new nhn.husky.MessageManager(oMessageMap));
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_QuickEditor_Common(elAppContainer)); // 퀵에디터 공통(표, 이미지)
|
||||
|
||||
oEditor.registerPlugin(new nhn.husky.SE2B_CSSLoader()); // CSS lazy load
|
||||
if(window.frameElement){
|
||||
oEditor.registerPlugin(new nhn.husky.SE_OuterIFrameControl(elAppContainer, 100));
|
||||
}
|
||||
|
||||
oEditor.registerPlugin(new nhn.husky.SE_ToolbarToggler(elAppContainer, htParams.bUseToolbar));
|
||||
oEditor.registerPlugin(new nhn.husky.SE2M_Accessibility(elAppContainer)); // 에디터내의 웹접근성 관련 기능모음 플러그인
|
||||
|
||||
return oEditor;
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Smart Editor 2 Configuration : This setting must be changed by service
|
||||
*/
|
||||
window.nhn = window.nhn || {};
|
||||
nhn.husky = nhn.husky || {};
|
||||
nhn.husky.SE2M_Configuration = nhn.husky.SE2M_Configuration || {};
|
||||
|
||||
/**
|
||||
* CSS LazyLoad를 위한 경로
|
||||
*/
|
||||
nhn.husky.SE2M_Configuration.SE2B_CSSLoader = {
|
||||
sCSSBaseURI : "css"
|
||||
};
|
||||
|
||||
/**
|
||||
* 편집영역 설정
|
||||
*/
|
||||
nhn.husky.SE2M_Configuration.SE_EditingAreaManager = {
|
||||
sCSSBaseURI : "css", // smart_editor2_inputarea.html 파일의 상대경로
|
||||
sBlankPageURL : "smart_editor2_inputarea.html",
|
||||
sBlankPageURL_EmulateIE7 : "smart_editor2_inputarea_ie8.html",
|
||||
aAddtionalEmulateIE7 : [] // IE8 default 사용, IE9 ~ 선택적 사용
|
||||
};
|
||||
|
||||
/**
|
||||
* [웹접근성]
|
||||
* 단축키 ALT+, ALT+. 을 이용하여 스마트에디터 영역의 이전/이후 요소로 이동할 수 있다.
|
||||
* sBeforeElementId : 스마트에디터 영역 이전 요소의 id
|
||||
* sNextElementId : 스마트에디터 영역 이후 요소의 id
|
||||
*
|
||||
* 스마트에디터 영역 이외의 제목 영역 (예:스마트에디터가 적용된 블로그 쓰기 페이지에서의 제목 영역) 에 해당하는 엘리먼트에서 Tab키를 누르면 에디팅 영역으로 포커스를 이동시킬 수 있다.
|
||||
* sTitleElementId : 제목에 해당하는 input 요소의 id.
|
||||
*/
|
||||
nhn.husky.SE2M_Configuration.SE2M_Accessibility = {
|
||||
sBeforeElementId : '',
|
||||
sNextElementId : '',
|
||||
sTitleElementId : ''
|
||||
};
|
||||
|
||||
/**
|
||||
* 링크 기능 옵션
|
||||
*/
|
||||
nhn.husky.SE2M_Configuration.SE2M_Hyperlink = {
|
||||
bAutolink : true // 자동링크기능 사용여부(기본값:true)
|
||||
};
|
||||
|
||||
nhn.husky.SE2M_Configuration.Quote = {
|
||||
sImageBaseURL : 'http://static.se2.naver.com/static/img'
|
||||
};
|
||||
nhn.husky.SE2M_Configuration.SE2M_ColorPalette = {
|
||||
bAddRecentColorFromDefault : false
|
||||
};
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
SmartEditor Basic 2.0 릴리즈 패키지
|
||||
|
||||
SmartEdtitor™는 Javascript로 구현된 웹 기반의 WYSIWYG 에디터입니다. SmartEdtitor™는 WYSIWYG 모드 및 HTML 편집 모드와 TEXT 모드를 제공하고, 자유로운 폰트 크기 설정 기능, 줄 간격 설정 기능, 단어 찾기/바꾸기 기능 등 편집에 필요한 다양한 기능을 제공하므로 사용자들은 SmartEdtitor™를 사용하여 쉽고 편리하게 원하는 형태의 글을 작성할 수 있습니다.
|
||||
또한, SmartEdtitor™의 구조는 기능을 쉽게 추가할 수 있는 플러그인 구조로 되어 있어 정해진 규칙에 따라 플러그인을 만들기만 하면 됩니다.
|
||||
|
||||
현재 SmartEdtitor™는 네이버, 한게임 등 NHN의 주요 서비스에 적용되어 있습니다.
|
||||
지원하는 브라우저 환경은 아래와 같으며 지속적으로 지원 대상 브라우저를 확장할 예정입니다.
|
||||
|
||||
* 지원하는 브라우저
|
||||
Internet Explorer 7.0+ / 10.0-
|
||||
FireFox 3.5+
|
||||
Safari 4.0+
|
||||
Chrome 4.0+
|
||||
|
||||
또한 지속적인 기능 추가를 통해 편리하고 강력한 에디터로 거듭날 것입니다.
|
||||
|
||||
라이센스 : LGPL v2
|
||||
홈페이지 : http://dev.naver.com/projects/smarteditor
|
||||
|
||||
===================================================================================
|
||||
|
||||
릴리즈 패키지에 포함된 파일은 아래와 같습니다.
|
||||
/css : 에디터에서 사용하는 css 파일
|
||||
/img : 에디터에서 사용하는 이미지 파일
|
||||
/js : 에디터를 적용할 때 사용하는 JS 파일
|
||||
/photo_uploader : 사진 퀵 업로더 팝업 UI를 구성하는 파일
|
||||
readme.txt : 간략한 설명
|
||||
release_notes.txt : 릴리즈 노트
|
||||
sample.php : SmartEditor2.html을 이용해 편집한 내용을 서버에서 받는 php 예제
|
||||
smart_editor2_inputarea.html : 에디터의 편집 영역을 나타내는 HTML로 에디터를 적용할 때 반드시 필요
|
||||
smart_editor2_inputarea_ie8.html : smart_editor2_inputarea.html와 동일한 기능이나 사용자의 브라우저 Internet Explorer 8.x 이상인 경우에 사용
|
||||
SmartEditor2.html : 에디터 데모 페이지. 에디터 적용 시에도 참고 할 수 있다.
|
||||
SmartEditor2Skin.html : 에디터를 적용한 페이지에서 로드하는 에디터의 스킨 HTML 파일로 에디터에서 사용하는 JS 파일과 css 파일을 링크하며 에디터의 마크업을 가지고 있다. SmartEditor2.html 에서도 확인할 수 있다.
|
||||
src_include.txt : 자바스크립트 플러그인 소스를 직접 수정하고자 할 경우 참고할 수 있는 파일
|
||||
|
||||
===================================================================================
|
||||
|
||||
사용 중 불편한 점이 있거나 버그를 발견하는 경우 SmartEdtitor™ 프로젝트의 이슈에 올려 주세요~~~
|
||||
http://dev.naver.com/projects/smarteditor/issue
|
||||
여기입니다! :)
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
==============================================================================================
|
||||
2.3.10_임시
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- 크롬 > 밑줄 선택 글작성하다 취소선 선택하고 밑줄 선택을 취소한 경우 툴바에 반영되지 않는 문제
|
||||
- 굵게/밑줄/기울림/취소선이 있는 상태에서 엔터치고 폰트크기 수정하면 이전 폰트크기로 줄간격이 유지되는 문제
|
||||
- 외부프로그램 테이블 복사 붙여넣기 관련 오류 수정
|
||||
- IE8이하 > 글자크기 지정 후 엔터를 치면 커서위치가 위로 올라감
|
||||
- IE9이상 > 글꼴 효과를 미리 지정 한 후에 텍스트 입력 시, 색상 변경은 적용되나 굵게 기울임 밑줄 취소선 등의 효과는 적용안됨
|
||||
- [FF]밑줄 선택> 내용입력 후 엔터>밑줄 취소 후 내용 입력>마우스로 커서 클릭 후 내용 계속 입력 시 밑줄이 있는 글로 노출됨
|
||||
- [FF] 메모장에서 작성한 내용을 붙여넣기 후 엔터 > 내용입력 > 엔터 했을 때 줄바꿈이 되지 않는 현상
|
||||
- HTML5 > 글자를 선택하여 폰트크기 지정시 굵게/밑줄/기울림/취소선이 있으면 이전에 적용한 폰트크기 기준으로 줄간격이 유지되는 문제
|
||||
|
||||
2. 기능 개선
|
||||
- IE에서 자동으로 공백이 삽입되는 문제
|
||||
- MacOS > 사파리 > 외부프로그램 테이블 붙여넣기 개선
|
||||
|
||||
3. 보안 패치
|
||||
- 사진첨부 샘플의 null byte injection 취약점 보완
|
||||
|
||||
==============================================================================================
|
||||
2.3.10
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- 크롬 > 브라우저 확대축소시 폰트크기가 잘못 나오는 이슈
|
||||
- IE > 표삽입>임의로 두개 칸 선택하여 셀 병합>행삽입 클릭 시 JS 오류 발생
|
||||
- IE11 > 호환성 보기를 설정하지 않을 경우 글꼴목록이 선택되지 않는 문제 수정
|
||||
|
||||
2. 기능 개선
|
||||
- 외부프로그램 테이블 복사 붙여넣기 개선
|
||||
- 입력창 조절 안내 레이어를 주석처리하면 스크립트 오류 발생
|
||||
|
||||
==============================================================================================
|
||||
2.3.9
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- 파이어폭스에서 에디팅시 스타일깨짐 등 오작동
|
||||
- Chrome > 찾기/바꾸기 > 모두바꾸기 버튼 클릭시 찾을단어가 지워지지 않고 남아있음
|
||||
|
||||
2. 기능 개선
|
||||
- 링크 > 자동링크 설정/해제 옵션 추가
|
||||
- [IE11] WYSIWYG 모드와 HTML 모드를 오갈 때마다 문서의 마지막에 비정상적인 <BR>이 첨가됩니다.
|
||||
- [웹접근성] 빠져나가기 단축키 기능 개선
|
||||
|
||||
==============================================================================================
|
||||
2.3.8
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- 테이블 내부 영역을 Shift + 클릭으로 선택 후 정렬하고 HTML 로 전환하면 더미 P 태그가 생성되는 문제 수정
|
||||
- 테이블 내부 영역 선택 혹은 에디터 내용 전체 선택 후 정렬 시 동작안함
|
||||
- [IE10, IE11] 표의 셀을 드래그했을 때 블럭 지정이 되지 않는 현상
|
||||
- HTML 모드 변환시 태그 자동 정렬에 의한 버그
|
||||
|
||||
2. 기능 개선
|
||||
- [MacOS 대응] 폰트변경이슈
|
||||
|
||||
==============================================================================================
|
||||
2.3.7
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- 에디터에 표 생성 후 일부 셀 선택하여 배경색 설정> 배경색 설정된 셀 선택 후 셀 삽입 시 색상이 삽입되지 않습니다.
|
||||
- [IE9특정] 글 작성 중 번호매기기 또는 글머리 적용 후 정렬방식을 변경하면 엔터키 누를 시 커서가 한줄 떨어져서 노출됩니다.
|
||||
- [IE10] 표 생성 후 표 드래그 시 셀의 너비/높이가 늘어나는 현상
|
||||
|
||||
2. 기능 개선
|
||||
- IE11 대응
|
||||
- 특수기호 삽입시 커서 위치가 뒤쪽으로 나오도록 개선
|
||||
- 커서에 활성화된 글꼴 확인 로직 개선
|
||||
|
||||
==============================================================================================
|
||||
2.3.6
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- 글 작성 후 번호매기기 적용하고 엔터키 수행하는 경우 JS 오류가 발생하는 현상 수정
|
||||
|
||||
==============================================================================================
|
||||
2.3.5
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 기능 개선
|
||||
- 줄간격 설정 시 값을 직접 입력하는 경우 줄간격의 최소값 적용
|
||||
|
||||
==============================================================================================
|
||||
2.3.4
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- [IE9/10] pre 태그의 바로 다음에 \n이 존재하는 경우 개행이 되지 않는 이슈 해결
|
||||
- 입력창 크기 조절바 사용 여부 오류 해결
|
||||
- 사진 퀵 업로더 모듈 오타 수정 ($newPath -> $new_path)
|
||||
|
||||
2. 기능 개선
|
||||
- 글꼴 목록에 글꼴 종류 추가하기 기능 (SmartEditor2.html 참조)
|
||||
- 사진 퀵 업로더 모듈에 이미지 파일 확장자 체크 추가
|
||||
|
||||
==============================================================================================
|
||||
2.3.3
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- IE9 에서 템플릿을 적용한 표 생성 후 일부의 셀을 드래그하는 경우 셀의 높이가 늘어나는 현상 수정
|
||||
|
||||
2. 기능 개선
|
||||
- MAC OS의 CMD 키로 Ctrl 단축키 기능 적용 확장
|
||||
- 기본 글꼴 종류 추가 (Courier New, 나눔고딕 코딩)
|
||||
|
||||
==============================================================================================
|
||||
2.3.1
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 기능 개선
|
||||
- [웹접근성] 글쓰기 영역의 iframe의 title속성에 단축키 설명 제공
|
||||
- [웹접근성] 제목 input영역에서 제목 입력 후 TAB하면 스마트에디터 편집 영역으로 포커스 이동하는 기능 추가
|
||||
- [웹접근성] 툴바 영역의 이전/다음 아이템 이동을 TAB, SHIFT+TAB으로 이동할 수 있도록 추가
|
||||
|
||||
==============================================================================================
|
||||
2.3.0
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 기능 개선
|
||||
- [웹접근성] 키보드로만 메뉴를 이동할 수 있도록 단축키 적용
|
||||
- [웹접근성] 웹접근성 도움말 제공
|
||||
- 편집모드와 사이즈 조절바 사용 옵션 추가
|
||||
- 사진 첨부 팝업 데모 파일 구조 개선
|
||||
|
||||
==============================================================================================
|
||||
2.2.1
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- 사진 퀵 업로더 추가 시, 가이드 대로 수행했을 때 사진 첨부가 2번 실행되는 문제 해결
|
||||
: loader-min.js 파일 내에 사진 퀵 업로더 소스가 포함되어 있던 부분 제거하여 소스 분리
|
||||
|
||||
2. 기능 개선
|
||||
- 툴바의 기능 제거/순서 변경이 쉽도록 마크업 구조 개선
|
||||
※ 툴바의 기능 제거/순서 변경은 가이드 문서를 참고하세요.
|
||||
|
||||
|
||||
3. 폴더/파일 변경
|
||||
- /js_src 폴더 제거
|
||||
- /js/smarteditor2.js 추가
|
||||
: /js_src 폴더를 /js/smarteditor2.js 로 대체했습니다.
|
||||
: /js_src 폴더 구조에서 사용자가 소스를 검색하여 수정하기 어렵던 부분을 보완하기 위하여
|
||||
: /js_src 폴더 내의 플러그인 소스를 통합한 /js/smarteditor2.js 를 추가했습니다.
|
||||
- /js/loader-min.js 제거
|
||||
- /js/smarteditor2.min.js 추가
|
||||
: /js/loader-min.js 파일을 /js/smarteditor2.min.js로 대체했습니다.
|
||||
- /quick_photo_uploader 폴더 추가
|
||||
- /popup 폴더 이동
|
||||
: /popup 폴더 - 사진 퀵 업로더의 팝업과 관련된 소스
|
||||
: /plugin 폴더 - 사진 퀵 업로더의 사진첨부를 처리하는 플러그인 js 소스
|
||||
- /img/ko_KR 폴더 추가
|
||||
: 이후의 다국어 버전 지원을 위하여 이미지 폴더 내 디렉토리가 추가되었습니다.
|
||||
: 언어 별 구분이 필요없는 이미지는 /img 바로 하위에 두었고,
|
||||
: 언어 별로 구분되어야 하는 이미지는 /img/ko_KR 과 같이 언어 별 디렉토리로 구분했습니다.
|
||||
: 버전 업그레이드를 하는 경우 이미지 경로가 변경된 점에 주의하시기 바랍니다.
|
||||
- /js/SE2B_Configuration.js 제거
|
||||
- /js/SE2B_Configuration_Service.js 추가
|
||||
- /js/SE2B_Configuration_General.js 추가
|
||||
: /js/SE2B_Configuration_Service.js 와 /js/SE2B_Configuration_General.js로 파일 분리했습니다.
|
||||
: /js/SE2B_Configuration_Service.js 는 적용을 할 때 사용자가 변경할 가능성이 높은 플러그인 설정을 갖고,
|
||||
: /js/SE2B_Configuration_General.js 는 서비스에 적용할 때 변경할 가능성이 거의 없는 설정입니다.
|
||||
|
||||
==============================================================================================
|
||||
2.1.3
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- [Chrome] 보기 페이지에 글자색이 설정되어 있는 경우 글 작성 시 내용에 적용한 글자색으로 노출되지 않는 문제 해결
|
||||
- 엔터 처리가 <BR>로 설정된 경우에도 텍스트 모드에서 모드변경 혹은 글 저장할 때 개행이 <P>로 표시되는 문제 해결
|
||||
- [IE9] 각주 삽입 시, 하단으로 떨어지는 이슈 해결
|
||||
- [Chrome] 인용구 밖에 글머리기호/번호매기기가 있을 때 인용구 안에서 글머리기호/번호매기기 시 내용이 인용구 밖으로 나가는 문제 해결
|
||||
- [IE] IE에서 특정 블로그 글을 복사하여 붙여넣기 했을 때 개행이 제거되는 문제 해결
|
||||
- 사진을 드래그해서 사이즈를 변경한 후 저장 혹은 HTML모드로 변경하면, 사진 사이즈가 원복되는 현상 해결
|
||||
- [Chrome/FF/Safari] 스크롤바가 생성되도록 문자입력 후 엔터 클릭하지 않은 상태에서 이미지 하나 삽입 시 이미지에 포커싱이 놓이지 않는 문제 해결
|
||||
- [IE9 표준] 사진을 스크롤로 일부 가린 상태에서 재편집하여 적용했을 때 계속 가려진 상태인 문제 해결
|
||||
- FF에서 사진을 여러장 첨부 시 스크롤이 가장 마지막 추가한 사진으로 내려가지 않음 해결
|
||||
- 호환 모드를 제거하고 사진 첨부 시 에디팅 영역의 커서 주위에 <sub><sup> 태그가 붙어서 글자가 매우 작게 되는 현상 해결
|
||||
- [IE9] 에디터에 각주 연속으로 입력 시 커서가 각주사이로 이동되는 현상 해결
|
||||
- 글꼴색/글꼴배경색 더보기에서 글꼴색 선택>다시 다른 색상 선택 후 처음 선택되었던 색상 선택 시 처음 선택색상이 원래 자리에서 삭제되지 않는 현상 해결
|
||||
- 제공하지 않는 기능인 이모티콘 플러그인 소스 제거
|
||||
- 플러그인 태그 코드 추가 시 <li> 태그와 <button> 태그 사이에 개행이 있으면 이벤트가 등록되지 않는 현상 해결
|
||||
|
||||
2. 기능 개선
|
||||
- 표 삽입 시 본문 작성 영역 안에 너비 100%로 생성되도록 개선
|
||||
- 호환모드 설정이 설정 파일 정보에 따라 처리되도록 변경
|
||||
|
||||
|
||||
==============================================================================================
|
||||
2.1.2
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 버그 수정
|
||||
- [IE9]Shift+Enter를 여러번 하고 글의 중간의 마지막 글자 다음에서 엔터를 쳤을 때 엔터 위치가 달라지는 현상 수정
|
||||
- [IE9]메모장에서 붙여 넣기 후 내용 중간의 마지막 글자 다음에서 엔터를 쳤을 때 엔터 위치가 달라지는 현상 수정
|
||||
- 한 줄 입력 후 색상을 적용하고 내용 중간에서 엔터를 쳤을 때 적용되었던 색상이 풀리던 현상 수정
|
||||
- 글꼴 레이어를 열었을 때, 샘플 텍스트가 잘못 나오던 현상 수정
|
||||
- 인용구를 14개까지 중첩하고, 15개부터 경고 창이 나오도록 수정
|
||||
|
||||
2. 기능 개선
|
||||
- 찾기/바꾸기 레이어를 닫았다가 다시 열 때, [바꿀 단어] 입력란이 초기화 되도록 개선
|
||||
- 찾기/바꾸기 레이어 오픈 시 툴바 버튼 inactive 처리
|
||||
- 표 추가 레이어의 테이블 색상, 배경 색상의 기본 값을 SmartEditor2Skin.html에서 변경할 수 있도록 함
|
||||
※주의 : 기존의 html파일에 덮어 씌우게 되면 기본 배경 색상이 다르게 표시됨
|
||||
따라서 반드시 새로 업데이트 된 html 파일을 사용하기를 권장
|
||||
임의로 수정하려면 위 파일의 아래 부분의 value를 아래와 같이 변경해야 함
|
||||
<input id="se2_b_color" name="" type="text" maxlength="7" value="#cccccc" class="input_ty3">
|
||||
<input id="se2_cellbg" name="" type="text" maxlength="7" value="#ffffff" class="input_ty3">
|
||||
|
||||
|
||||
==============================================================================================
|
||||
2.1.1
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 기능 추가
|
||||
- 에디터 로딩 완료 시점에 실행되는 함수 (fOnAppLoad) 정의
|
||||
|
||||
2. 버그 수정
|
||||
- 에디터 초기 Width에 100%가 설정될 수 있도록 수정, minWidth 설정 추가
|
||||
- 마크업에서 나눔 글꼴을 제외하면 JS 에러가 나는 문제 수정
|
||||
- [IE9] 글자 색상 적용 후 내용 중간에서 계속 Enter할 때 Enter가 되지 않는 오류 수정
|
||||
- [Chrome/Safari] 표 간단편집기 위에서 text를 drag하면 JS 에러가 발생하는 문제 수정
|
||||
|
||||
3. 기능 개선
|
||||
- 사진 퀵 업로더 : 쉽게 사용할 수 있도록 소스 수정 및 예제 보강
|
||||
|
||||
|
||||
==============================================================================================
|
||||
2.1.0
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 기능 추가
|
||||
- 사진 퀵 업로더 : 사진 첨부 팝업 UI 제공 (HTML5 지원)
|
||||
- 에디터 본문에 글 작성 후 창을 닫을 때 발생하는 alert 메세지를 사용자가 설정할 수 있도록 옵션을 추가함
|
||||
- Jindo 모듈을 패키지에 포함하도록 빌드를 수정함
|
||||
- document.domain을 제거함
|
||||
- 에디터 초기 Width를 설정할 수 있도록 수정함
|
||||
- 툴바의 접힘/펼침 기능을 제공하는 SE_ToolbarToggler 플러그인 추가함
|
||||
|
||||
2. 버그 수정
|
||||
- 에디터 리사이즈 시 북마크 태그가 본문에 추가되는 이슈 확인 및 수정함
|
||||
|
||||
|
||||
==============================================================================================
|
||||
2.0.0
|
||||
----------------------------------------------------------------------------------------------
|
||||
1. 기능 강화
|
||||
- 글꼴과 글자 크기
|
||||
: 기존의 Selectbox 형태의 글꼴 목록을 깔끔한 디자인의 레이어로 제공한다.
|
||||
- 글자색과 글자 배경색
|
||||
: 기존의 기본 색상표 이외에 다양한 색상을 선택할 수 있는 컬러 팔레트를 확장 지원한다.
|
||||
- 줄간격
|
||||
: 기존의 Selectbox 형태의 줄간격 목록을 깔끔한 디자인의 레이어로 제공한다.
|
||||
또한, 줄간격을 직접 설정할 수 있도록 직접 입력 기능도 확장 지원한다.
|
||||
- 인용구
|
||||
: 기존의 7가지에서 10가지로 인용구 디자인을 확장 지원한다.
|
||||
- 표
|
||||
: 표 생성 시 기존의 테두리 색상과 두께를 설정할 수 있는 기능 이외에 테두리 스타일을 설정할 수 있는 기능을 확장 지원한다.
|
||||
또한, 표 템플릿을 제공하여 보다 쉽게 표 스타일을 생성할 수 있도록 하였다.
|
||||
|
||||
2. 기능 추가
|
||||
- 표 간단편집기
|
||||
: 표 생성 후 스타일을 편집할 수 있도록 표 편집 기능을 추가 제공한다.
|
||||
- TEXT 모드
|
||||
: WYSIWYG와 HTML 모드 이외에 TEXT 모드를 제공하여 텍스트만으로 본문의 내용을 작성할 수 있도록 편집 모드를 추가 제공한다.
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="ko" xml:lang="ko">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Smart Editor™ WYSIWYG Mode</title>
|
||||
<link href="css/smart_editor2_in.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body class="smartOutput se2_inputarea">
|
||||
<p>
|
||||
<b><u>에디터 내용:</u></b>
|
||||
</p>
|
||||
|
||||
<div style="width:736px;">
|
||||
<?php
|
||||
$postMessage = $_POST["ir1"];
|
||||
echo $postMessage;
|
||||
?>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<p>
|
||||
<b><span style="color:#FF0000">주의: </span>sample.php는 샘플 파일로 정상 동작하지 않을 수 있습니다. 이 점 주의바랍니다.</b>
|
||||
</p>
|
||||
|
||||
<?php echo(htmlspecialchars_decode('<img id="test" width="0" height="0">'))?>
|
||||
|
||||
<script>
|
||||
if(!document.getElementById("test")) {
|
||||
alert("PHP가 실행되지 않았습니다. 내용을 로컬 파일로 전송한 것이 아니라 서버로 전송했는지 확인 해 주십시오.");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* @use 간단 포토 업로드용으로 제작되었습니다.
|
||||
* @author cielo
|
||||
* @See nhn.husky.SE2M_Configuration
|
||||
* @ 팝업 마크업은 SimplePhotoUpload.html과 SimplePhotoUpload_html5.html이 있습니다.
|
||||
*/
|
||||
|
||||
nhn.husky.SE2M_AttachQuickPhoto = jindo.$Class({
|
||||
name : "SE2M_AttachQuickPhoto",
|
||||
|
||||
$init : function(){},
|
||||
|
||||
$ON_MSG_APP_READY : function(){
|
||||
this.oApp.exec("REGISTER_UI_EVENT", ["photo_attach", "click", "ATTACHPHOTO_OPEN_WINDOW"]);
|
||||
},
|
||||
|
||||
$LOCAL_BEFORE_FIRST : function(sMsg){
|
||||
if(!!this.oPopupMgr){ return; }
|
||||
// Popup Manager에서 사용할 param
|
||||
this.htPopupOption = {
|
||||
oApp : this.oApp,
|
||||
sName : this.name,
|
||||
bScroll : false,
|
||||
sProperties : "",
|
||||
sUrl : ""
|
||||
};
|
||||
this.oPopupMgr = nhn.husky.PopUpManager.getInstance(this.oApp);
|
||||
},
|
||||
|
||||
/**
|
||||
* 포토 웹탑 오픈
|
||||
*/
|
||||
$ON_ATTACHPHOTO_OPEN_WINDOW : function(){
|
||||
this.htPopupOption.sUrl = this.makePopupURL();
|
||||
this.htPopupOption.sProperties = "left=0,top=0,width=403,height=359,scrollbars=yes,location=no,status=0,resizable=no";
|
||||
|
||||
this.oPopupWindow = this.oPopupMgr.openWindow(this.htPopupOption);
|
||||
|
||||
// 처음 로딩하고 IE에서 커서가 전혀 없는 경우
|
||||
// 복수 업로드시에 순서가 바뀜
|
||||
// [SMARTEDITORSUS-1698]
|
||||
this.oApp.exec('FOCUS', [true]);
|
||||
// --[SMARTEDITORSUS-1698]
|
||||
return (!!this.oPopupWindow ? true : false);
|
||||
},
|
||||
|
||||
/**
|
||||
* 서비스별로 팝업에 parameter를 추가하여 URL을 생성하는 함수
|
||||
* nhn.husky.SE2M_AttachQuickPhoto.prototype.makePopupURL로 덮어써서 사용하시면 됨.
|
||||
*/
|
||||
makePopupURL : function(){
|
||||
var sPopupUrl = "./sample/photo_uploader/photo_uploader.html";
|
||||
|
||||
return sPopupUrl;
|
||||
},
|
||||
|
||||
/**
|
||||
* 팝업에서 호출되는 메세지.
|
||||
*/
|
||||
$ON_SET_PHOTO : function(aPhotoData){
|
||||
var sContents,
|
||||
aPhotoInfo,
|
||||
htData;
|
||||
|
||||
if( !aPhotoData ){
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
sContents = "";
|
||||
for(var i = 0; i <aPhotoData.length; i++){
|
||||
htData = aPhotoData[i];
|
||||
|
||||
if(!htData.sAlign){
|
||||
htData.sAlign = "";
|
||||
}
|
||||
|
||||
aPhotoInfo = {
|
||||
sName : htData.sFileName || "",
|
||||
sOriginalImageURL : htData.sFileURL,
|
||||
bNewLine : htData.bNewLine || false
|
||||
};
|
||||
|
||||
sContents += this._getPhotoTag(aPhotoInfo);
|
||||
}
|
||||
|
||||
this.oApp.exec("PASTE_HTML", [sContents]); // 위즐 첨부 파일 부분 확인
|
||||
}catch(e){
|
||||
// upload시 error발생에 대해서 skip함
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @use 일반 포토 tag 생성
|
||||
*/
|
||||
_getPhotoTag : function(htPhotoInfo){
|
||||
// id와 class는 썸네일과 연관이 많습니다. 수정시 썸네일 영역도 Test
|
||||
var sTag = '<img src="{=sOriginalImageURL}" title="{=sName}" >';
|
||||
if(htPhotoInfo.bNewLine){
|
||||
sTag += '<br style="clear:both;">';
|
||||
}
|
||||
sTag = jindo.$Template(sTag).process(htPhotoInfo);
|
||||
|
||||
return sTag;
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,684 @@
|
|||
//변수 선언 및 초기화
|
||||
var nImageInfoCnt = 0;
|
||||
var htImageInfo = []; //image file정보 저장
|
||||
var aResult = [];
|
||||
|
||||
var rFilter = /^(image\/bmp|image\/gif|image\/jpg|image\/jpeg|image\/png)$/i;
|
||||
var rFilter2 = /^(bmp|gif|jpg|jpeg|png)$/i;
|
||||
var nTotalSize = 0;
|
||||
var nMaxImageSize = 10*1024*1024;
|
||||
var nMaxTotalImageSize = 50*1024*1024;
|
||||
var nMaxImageCount = 10;
|
||||
var nImageFileCount = 0;
|
||||
var bSupportDragAndDropAPI = false;
|
||||
var oFileUploader;
|
||||
var bAttachEvent = false;
|
||||
|
||||
//마크업에 따른 할당
|
||||
var elContent= $("pop_content");
|
||||
var elDropArea = jindo.$$.getSingle(".drag_area",elContent);
|
||||
var elDropAreaUL = jindo.$$.getSingle(".lst_type",elContent);
|
||||
var elCountTxtTxt = jindo.$$.getSingle("#imageCountTxt",elContent);
|
||||
var elTotalSizeTxt = jindo.$$.getSingle("#totalSizeTxt",elContent);
|
||||
var elTextGuide = $("guide_text");
|
||||
var welUploadInputBox = $Element("uploadInputBox");
|
||||
var oNavigator = jindo.$Agent().navigator();
|
||||
|
||||
//마크업-공통
|
||||
var welBtnConfirm = $Element("btn_confirm"); //확인 버튼
|
||||
var welBtnCancel= $Element("btn_cancel"); //취소 버튼
|
||||
|
||||
//진도로 랩핑된 element
|
||||
var welTextGuide = $Element(elTextGuide);
|
||||
var welDropArea = $Element(elDropArea);
|
||||
var welDropAreaUL = $Element(elDropAreaUL);
|
||||
var fnUploadImage = null;
|
||||
|
||||
//File API 지원 여부로 결정
|
||||
function checkDragAndDropAPI(){
|
||||
try{
|
||||
if( !oNavigator.ie ){
|
||||
if(!!oNavigator.safari && oNavigator.version <= 5){
|
||||
bSupportDragAndDropAPI = false;
|
||||
}else{
|
||||
bSupportDragAndDropAPI = true;
|
||||
}
|
||||
} else {
|
||||
bSupportDragAndDropAPI = false;
|
||||
}
|
||||
}catch(e){
|
||||
bSupportDragAndDropAPI = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------- html5 미지원 브라우저에서 (IE9 이하) ---------------
|
||||
/**
|
||||
* 이미지를 첨부 후 활성화된 버튼 상태
|
||||
*/
|
||||
function goStartMode(){
|
||||
var sSrc = welBtnConfirm.attr("src")|| "";
|
||||
if(sSrc.indexOf("btn_confirm2.png") < 0 ){
|
||||
welBtnConfirm.attr("src","./img/btn_confirm2.png");
|
||||
fnUploadImage.attach(welBtnConfirm.$value(), "click");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 이미지를 첨부 전 비활성화된 버튼 상태
|
||||
* @return
|
||||
*/
|
||||
function goReadyMode(){
|
||||
var sSrc = welBtnConfirm.attr("src")|| "";
|
||||
if(sSrc.indexOf("btn_confirm2.png") >= 0 ){
|
||||
fnUploadImage.detach(welBtnConfirm.$value(), "click");
|
||||
welBtnConfirm.attr("src","./img/btn_confirm.png");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 일반 업로드
|
||||
* @desc oFileUploader의 upload함수를 호출함.
|
||||
*/
|
||||
function generalUpload(){
|
||||
oFileUploader.upload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 첨부 전 안내 텍스트가 나오는 배경으로 '설정'하는 함수.
|
||||
* @return
|
||||
*/
|
||||
function readyModeBG (){
|
||||
var sClass = welTextGuide.className();
|
||||
if(sClass.indexOf('nobg') >= 0){
|
||||
welTextGuide.removeClass('nobg');
|
||||
welTextGuide.className('bg');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 첨부 전 안내 텍스트가 나오는 배경을 '제거'하는 함수.
|
||||
* @return
|
||||
*/
|
||||
function startModeBG (){
|
||||
var sClass = welTextGuide.className();
|
||||
if(sClass.indexOf('nobg') < 0){
|
||||
welTextGuide.removeClass('bg');
|
||||
welTextGuide.className('nobg');
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- html5 지원되는 브라우저에서 사용하는 함수 --------------------------
|
||||
/**
|
||||
* 팝업에 노출될 업로드 예정 사진의 수.
|
||||
* @param {Object} nCount 현재 업로드 예정인 사진 장수
|
||||
* @param {Object} nVariable 삭제되는 수
|
||||
*/
|
||||
function updateViewCount (nCount, nVariable){
|
||||
var nCnt = nCount + nVariable;
|
||||
elCountTxtTxt.innerHTML = nCnt +"장";
|
||||
nImageFileCount = nCnt;
|
||||
return nCnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 팝업에 노출될 업로드될 사진 총 용량
|
||||
*/
|
||||
function updateViewTotalSize(){
|
||||
var nViewTotalSize = Number(parseInt((nTotalSize || 0), 10) / (1024*1024));
|
||||
elTotalSizeTxt.innerHTML = nViewTotalSize.toFixed(2) +"MB";
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 전체 용량 재계산.
|
||||
* @param {Object} sParentId
|
||||
*/
|
||||
function refreshTotalImageSize(sParentId){
|
||||
var nDelImgSize = htImageInfo[sParentId].size;
|
||||
if(nTotalSize - nDelImgSize > -1 ){
|
||||
nTotalSize = nTotalSize - nDelImgSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hash table에서 이미지 정보 초기화.
|
||||
* @param {Object} sParentId
|
||||
*/
|
||||
function removeImageInfo (sParentId){
|
||||
//삭제된 이미지의 공간을 초기화 한다.
|
||||
htImageInfo[sParentId] = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* byte로 받은 이미지 용량을 화면에 표시를 위해 포맷팅
|
||||
* @param {Object} nByte
|
||||
*/
|
||||
function setUnitString (nByte) {
|
||||
var nImageSize;
|
||||
var sUnit;
|
||||
|
||||
if(nByte < 0 ){
|
||||
nByte = 0;
|
||||
}
|
||||
|
||||
if( nByte < 1024) {
|
||||
nImageSize = Number(nByte);
|
||||
sUnit = 'B';
|
||||
return nImageSize + sUnit;
|
||||
} else if( nByte > (1024*1024)) {
|
||||
nImageSize = Number(parseInt((nByte || 0), 10) / (1024*1024));
|
||||
sUnit = 'MB';
|
||||
return nImageSize.toFixed(2) + sUnit;
|
||||
} else {
|
||||
nImageSize = Number(parseInt((nByte || 0), 10) / 1024);
|
||||
sUnit = 'KB';
|
||||
return nImageSize.toFixed(0) + sUnit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면 목록에 적당하게 이름을 잘라서 표시.
|
||||
* @param {Object} sName 파일명
|
||||
* @param {Object} nMaxLng 최대 길이
|
||||
*/
|
||||
function cuttingNameByLength (sName, nMaxLng) {
|
||||
var sTemp, nIndex;
|
||||
if(sName.length > nMaxLng){
|
||||
nIndex = sName.indexOf(".");
|
||||
sTemp = sName.substring(0,nMaxLng) + "..." + sName.substring(nIndex,sName.length) ;
|
||||
} else {
|
||||
sTemp = sName;
|
||||
}
|
||||
return sTemp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total Image Size를 체크해서 추가로 이미지를 넣을지 말지를 결정함.
|
||||
* @param {Object} nByte
|
||||
*/
|
||||
function checkTotalImageSize(nByte){
|
||||
if( nTotalSize + nByte < nMaxTotalImageSize){
|
||||
nTotalSize = nTotalSize + nByte;
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 이벤트 핸들러 할당
|
||||
function dragEnter(ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
function dragExit(ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
function dragOver(ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* 드랍 영역에 사진을 떨구는 순간 발생하는 이벤트
|
||||
* @param {Object} ev
|
||||
*/
|
||||
function drop(ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
|
||||
if (nImageFileCount >= 10){
|
||||
alert("최대 10장까지만 등록할 수 있습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof ev.dataTransfer.files == 'undefined'){
|
||||
alert("HTML5를 지원하지 않는 브라우저입니다.");
|
||||
}else{
|
||||
//변수 선언
|
||||
var wel,
|
||||
files,
|
||||
nCount,
|
||||
sListTag = '';
|
||||
|
||||
//초기화
|
||||
files = ev.dataTransfer.files;
|
||||
nCount = files.length;
|
||||
|
||||
if (!!files && nCount === 0){
|
||||
//파일이 아닌, 웹페이지에서 이미지를 드래서 놓는 경우.
|
||||
alert("정상적인 첨부방식이 아닙니다.");
|
||||
return ;
|
||||
}
|
||||
|
||||
for (var i = 0, j = nImageFileCount ; i < nCount ; i++){
|
||||
if (!rFilter.test(files[i].type)) {
|
||||
alert("이미지파일 (jpg,gif,png,bmp)만 업로드 가능합니다.");
|
||||
} else if(files[i].size > nMaxImageSize){
|
||||
alert("이미지 용량이 10MB를 초과하여 등록할 수 없습니다.");
|
||||
} else {
|
||||
//제한된 수만 업로드 가능.
|
||||
if ( j < nMaxImageCount ){
|
||||
sListTag += addImage(files[i]);
|
||||
|
||||
//다음 사진을위한 셋팅
|
||||
j = j+1;
|
||||
nImageInfoCnt = nImageInfoCnt+1;
|
||||
} else {
|
||||
alert("최대 10장까지만 등록할 수 있습니다.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(j > 0){
|
||||
//배경 이미지 변경
|
||||
startModeBG();
|
||||
if ( sListTag.length > 1){
|
||||
welDropAreaUL.prependHTML(sListTag);
|
||||
}
|
||||
//이미지 총사이즈 view update
|
||||
updateViewTotalSize();
|
||||
//이미치 총 수 view update
|
||||
nImageFileCount = j;
|
||||
updateViewCount(nImageFileCount, 0);
|
||||
// 저장 버튼 활성화
|
||||
goStartMode();
|
||||
}else{
|
||||
readyModeBG();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지를 추가하기 위해서 file을 저장하고, 목록에 보여주기 위해서 string을 만드는 함수.
|
||||
* @param ofile 한개의 이미지 파일
|
||||
* @return
|
||||
*/
|
||||
function addImage(ofile){
|
||||
//파일 사이즈
|
||||
var ofile = ofile,
|
||||
sFileSize = 0,
|
||||
sFileName = "",
|
||||
sLiTag = "",
|
||||
bExceedLimitTotalSize = false,
|
||||
aFileList = [];
|
||||
|
||||
sFileSize = setUnitString(ofile.size);
|
||||
sFileName = cuttingNameByLength(ofile.name, 15);
|
||||
bExceedLimitTotalSize = checkTotalImageSize(ofile.size);
|
||||
|
||||
if( !!bExceedLimitTotalSize ){
|
||||
alert("전체 이미지 용량이 50MB를 초과하여 등록할 수 없습니다. \n\n (파일명 : "+sFileName+", 사이즈 : "+sFileSize+")");
|
||||
} else {
|
||||
//이미지 정보 저장
|
||||
htImageInfo['img'+nImageInfoCnt] = ofile;
|
||||
|
||||
//List 마크업 생성하기
|
||||
aFileList.push(' <li id="img'+nImageInfoCnt+'" class="imgLi"><span>'+ sFileName +'</span>');
|
||||
aFileList.push(' <em>'+ sFileSize +'</em>');
|
||||
aFileList.push(' <a onclick="delImage(\'img'+nImageInfoCnt+'\')"><img class="del_button" src="./img/btn_del.png" width="14" height="13" alt="첨부 사진 삭제"></a>');
|
||||
aFileList.push(' </li> ');
|
||||
|
||||
sLiTag = aFileList.join(" ");
|
||||
aFileList = [];
|
||||
}
|
||||
return sLiTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 DragAndDrop으로 사진을 추가하고, 확인버튼을 누른 경우에 동작한다.
|
||||
* @return
|
||||
*/
|
||||
function html5Upload() {
|
||||
var tempFile,
|
||||
sUploadURL;
|
||||
|
||||
//sUploadURL= 'file_uploader_html5.php'; //upload URL
|
||||
sUploadURL= 'file_uploader_html5.jsp'; //upload URL
|
||||
|
||||
//파일을 하나씩 보내고, 결과를 받음.
|
||||
for(var j=0, k=0; j < nImageInfoCnt; j++) {
|
||||
tempFile = htImageInfo['img'+j];
|
||||
try{
|
||||
if(!!tempFile){
|
||||
//Ajax통신하는 부분. 파일과 업로더할 url을 전달한다.
|
||||
callAjaxForHTML5(tempFile,sUploadURL);
|
||||
k += 1;
|
||||
}
|
||||
}catch(e){}
|
||||
tempFile = null;
|
||||
}
|
||||
}
|
||||
|
||||
function callAjaxForHTML5 (tempFile, sUploadURL){
|
||||
var oAjax = jindo.$Ajax(sUploadURL, {
|
||||
type: 'xhr',
|
||||
method : "post",
|
||||
onload : function(res){ // 요청이 완료되면 실행될 콜백 함수
|
||||
var sResString = res._response.responseText;
|
||||
if (res.readyState() == 4) {
|
||||
if(sResString.indexOf("NOTALLOW_") > -1){
|
||||
var sFileName = sResString.replace("NOTALLOW_", "");
|
||||
alert("이미지 파일(jpg,gif,png,bmp)만 업로드 하실 수 있습니다. ("+sFileName+")");
|
||||
}else{
|
||||
//성공 시에 responseText를 가지고 array로 만드는 부분.
|
||||
makeArrayFromString(res._response.responseText);
|
||||
}
|
||||
}
|
||||
},
|
||||
timeout : 3,
|
||||
onerror : jindo.$Fn(onAjaxError, this).bind()
|
||||
});
|
||||
oAjax.header("contentType","multipart/form-data");
|
||||
oAjax.header("file-name",encodeURIComponent(tempFile.name));
|
||||
oAjax.header("file-size",tempFile.size);
|
||||
oAjax.header("file-Type",tempFile.type);
|
||||
oAjax.request(tempFile);
|
||||
}
|
||||
|
||||
function makeArrayFromString(sResString){
|
||||
var aTemp = [],
|
||||
aSubTemp = [],
|
||||
htTemp = {}
|
||||
aResultleng = 0;
|
||||
|
||||
try{
|
||||
if(!sResString || sResString.indexOf("sFileURL") < 0){
|
||||
return ;
|
||||
}
|
||||
aTemp = sResString.split("&");
|
||||
for (var i = 0; i < aTemp.length ; i++){
|
||||
if( !!aTemp[i] && aTemp[i] != "" && aTemp[i].indexOf("=") > 0){
|
||||
aSubTemp = aTemp[i].split("=");
|
||||
htTemp[aSubTemp[0]] = aSubTemp[1];
|
||||
}
|
||||
}
|
||||
}catch(e){}
|
||||
|
||||
aResultleng = aResult.length;
|
||||
aResult[aResultleng] = htTemp;
|
||||
|
||||
if(aResult.length == nImageFileCount){
|
||||
setPhotoToEditor(aResult);
|
||||
aResult = null;
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 사진 삭제 시에 호출되는 함수
|
||||
* @param {Object} sParentId
|
||||
*/
|
||||
function delImage (sParentId){
|
||||
var elLi = jindo.$$.getSingle("#"+sParentId);
|
||||
|
||||
refreshTotalImageSize(sParentId);
|
||||
|
||||
updateViewTotalSize();
|
||||
updateViewCount(nImageFileCount,-1);
|
||||
//사진 file array에서 정보 삭제.
|
||||
removeImageInfo(sParentId);
|
||||
//해당 li삭제
|
||||
$Element(elLi).leave();
|
||||
|
||||
//마지막 이미지인경우.
|
||||
if(nImageFileCount === 0){
|
||||
readyModeBG();
|
||||
//사진 추가 버튼 비활성화
|
||||
goReadyMode();
|
||||
}
|
||||
|
||||
// drop 영역 이벤트 다시 활성화.
|
||||
if(!bAttachEvent){
|
||||
addEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 이벤트 할당
|
||||
*/
|
||||
function addEvent() {
|
||||
bAttachEvent = true;
|
||||
elDropArea.addEventListener("dragenter", dragEnter, false);
|
||||
elDropArea.addEventListener("dragexit", dragExit, false);
|
||||
elDropArea.addEventListener("dragover", dragOver, false);
|
||||
elDropArea.addEventListener("drop", drop, false);
|
||||
}
|
||||
|
||||
function removeEvent(){
|
||||
bAttachEvent = false;
|
||||
elDropArea.removeEventListener("dragenter", dragEnter, false);
|
||||
elDropArea.removeEventListener("dragexit", dragExit, false);
|
||||
elDropArea.removeEventListener("dragover", dragOver, false);
|
||||
elDropArea.removeEventListener("drop", drop, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax 통신 시 error가 발생할 때 처리하는 함수입니다.
|
||||
* @return
|
||||
*/
|
||||
function onAjaxError (){
|
||||
alert("[가이드]사진 업로더할 서버URL셋팅이 필요합니다.-onAjaxError");
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 업로드 시작
|
||||
* 확인 버튼 클릭하면 호출되는 msg
|
||||
*/
|
||||
function uploadImage (e){
|
||||
if(!bSupportDragAndDropAPI){
|
||||
generalUpload();
|
||||
}else{
|
||||
html5Upload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* jindo에 파일 업로드 사용.(iframe에 Form을 Submit하여 리프레시없이 파일을 업로드하는 컴포넌트)
|
||||
*/
|
||||
function callFileUploader (){
|
||||
oFileUploader = new jindo.FileUploader(jindo.$("uploadInputBox"),{
|
||||
// sUrl : location.href.replace(/\/[^\/]*$/, '') + '/file_uploader.php', //샘플 URL입니다.
|
||||
// sCallback : location.href.replace(/\/[^\/]*$/, '') + '/callback.html', //업로드 이후에 iframe이 redirect될 콜백페이지의 주소
|
||||
sUrl : '/imageUpload.do', //파일업로드를 처리하는 페이지
|
||||
sCallback : '/SE2/sample/photo_uploader/callback.html', //업로드 이후에 iframe이 redirect될 콜백페이지의 주소
|
||||
sFiletype : "*.jpg;*.png;*.bmp;*.gif", //허용할 파일의 형식. ex) "*", "*.*", "*.jpg", 구분자(;)
|
||||
sMsgNotAllowedExt : 'JPG, GIF, PNG, BMP 확장자만 가능합니다', //허용할 파일의 형식이 아닌경우에 띄워주는 경고창의 문구
|
||||
bAutoUpload : false, //파일이 선택됨과 동시에 자동으로 업로드를 수행할지 여부 (upload 메소드 수행)
|
||||
bAutoReset : true // 업로드한 직후에 파일폼을 리셋 시킬지 여부 (reset 메소드 수행)
|
||||
}).attach({
|
||||
select : function(oCustomEvent) {
|
||||
//파일 선택이 완료되었을 때 발생
|
||||
// oCustomEvent (이벤트 객체) = {
|
||||
// sValue (String) 선택된 File Input의 값
|
||||
// bAllowed (Boolean) 선택된 파일의 형식이 허용되는 형식인지 여부
|
||||
// sMsgNotAllowedExt (String) 허용되지 않는 파일 형식인 경우 띄워줄 경고메세지
|
||||
// }
|
||||
// 선택된 파일의 형식이 허용되는 경우만 처리
|
||||
if(oCustomEvent.bAllowed === true){
|
||||
goStartMode();
|
||||
}else{
|
||||
goReadyMode();
|
||||
oFileUploader.reset();
|
||||
}
|
||||
// bAllowed 값이 false인 경우 경고문구와 함께 alert 수행
|
||||
// oCustomEvent.stop(); 수행시 bAllowed 가 false이더라도 alert이 수행되지 않음
|
||||
},
|
||||
success : function(oCustomEvent) {
|
||||
// alert("success");
|
||||
// 업로드가 성공적으로 완료되었을 때 발생
|
||||
// oCustomEvent(이벤트 객체) = {
|
||||
// htResult (Object) 서버에서 전달해주는 결과 객체 (서버 설정에 따라 유동적으로 선택가능)
|
||||
// }
|
||||
var aResult = [];
|
||||
aResult[0] = oCustomEvent.htResult;
|
||||
setPhotoToEditor(aResult);
|
||||
//버튼 비활성화
|
||||
goReadyMode();
|
||||
oFileUploader.reset();
|
||||
window.close();
|
||||
},
|
||||
error : function(oCustomEvent) {
|
||||
//업로드가 실패했을 때 발생
|
||||
//oCustomEvent(이벤트 객체) = {
|
||||
// htResult : { (Object) 서버에서 전달해주는 결과 객체. 에러발생시 errstr 프로퍼티를 반드시 포함하도록 서버 응답을 설정하여야한다.
|
||||
// errstr : (String) 에러메시지
|
||||
// }
|
||||
//}
|
||||
//var wel = jindo.$Element("info");
|
||||
//wel.html(oCustomEvent.htResult.errstr);
|
||||
alert(oCustomEvent.htResult.errstr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지 닫기 버튼 클릭
|
||||
*/
|
||||
function closeWindow(){
|
||||
if(bSupportDragAndDropAPI){
|
||||
removeEvent();
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
|
||||
window.onload = function(){
|
||||
checkDragAndDropAPI();
|
||||
|
||||
if(bSupportDragAndDropAPI){
|
||||
$Element("pop_container2").hide();
|
||||
$Element("pop_container").show();
|
||||
|
||||
welTextGuide.removeClass("nobg");
|
||||
welTextGuide.className("bg");
|
||||
|
||||
addEvent();
|
||||
} else {
|
||||
$Element("pop_container").hide();
|
||||
$Element("pop_container2").show();
|
||||
callFileUploader();
|
||||
}
|
||||
fnUploadImage = $Fn(uploadImage,this);
|
||||
$Fn(closeWindow,this).attach(welBtnCancel.$value(), "click");
|
||||
};
|
||||
|
||||
/**
|
||||
* 서버로부터 받은 데이타를 에디터에 전달하고 창을 닫음.
|
||||
* @parameter aFileInfo [{},{},...]
|
||||
* @ex aFileInfo = [
|
||||
* {
|
||||
sFileName : "nmms_215646753.gif",
|
||||
sFileURL :"http://static.naver.net/www/u/2010/0611/nmms_215646753.gif",
|
||||
bNewLine : true
|
||||
},
|
||||
{
|
||||
sFileName : "btn_sch_over.gif",
|
||||
sFileURL :"http://static1.naver.net/w9/btn_sch_over.gif",
|
||||
bNewLine : true
|
||||
}
|
||||
* ]
|
||||
*/
|
||||
function setPhotoToEditor(oFileInfo){
|
||||
if (!!opener && !!opener.nhn && !!opener.nhn.husky && !!opener.nhn.husky.PopUpManager) {
|
||||
//스마트 에디터 플러그인을 통해서 넣는 방법 (oFileInfo는 Array)
|
||||
opener.nhn.husky.PopUpManager.setCallback(window, 'SET_PHOTO', [oFileInfo]);
|
||||
//본문에 바로 tag를 넣는 방법 (oFileInfo는 String으로 <img src=....> )
|
||||
//opener.nhn.husky.PopUpManager.setCallback(window, 'PASTE_HTML', [oFileInfo]);
|
||||
}
|
||||
}
|
||||
|
||||
// 2012.05 현재] jindo.$Ajax.prototype.request에서 file과 form을 지원하지 안함.
|
||||
jindo.$Ajax.prototype.request = function(oData) {
|
||||
this._status++;
|
||||
var t = this;
|
||||
var req = this._request;
|
||||
var opt = this._options;
|
||||
var data, v,a = [], data = "";
|
||||
var _timer = null;
|
||||
var url = this._url;
|
||||
this._is_abort = false;
|
||||
|
||||
if( opt.postBody && opt.type.toUpperCase()=="XHR" && opt.method.toUpperCase()!="GET"){
|
||||
if(typeof oData == 'string'){
|
||||
data = oData;
|
||||
}else{
|
||||
data = jindo.$Json(oData).toString();
|
||||
}
|
||||
}else if (typeof oData == "undefined" || !oData) {
|
||||
data = null;
|
||||
} else {
|
||||
data = oData;
|
||||
}
|
||||
|
||||
req.open(opt.method.toUpperCase(), url, opt.async);
|
||||
if (opt.sendheader) {
|
||||
if(!this._headers["Content-Type"]){
|
||||
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
|
||||
}
|
||||
req.setRequestHeader("charset", "utf-8");
|
||||
for (var x in this._headers) {
|
||||
if(this._headers.hasOwnProperty(x)){
|
||||
if (typeof this._headers[x] == "function")
|
||||
continue;
|
||||
req.setRequestHeader(x, String(this._headers[x]));
|
||||
}
|
||||
}
|
||||
}
|
||||
var navi = navigator.userAgent;
|
||||
if(req.addEventListener&&!(navi.indexOf("Opera") > -1)&&!(navi.indexOf("MSIE") > -1)){
|
||||
/*
|
||||
* opera 10.60에서 XMLHttpRequest에 addEventListener기 추가되었지만 정상적으로 동작하지 않아 opera는 무조건 dom1방식으로 지원함.
|
||||
* IE9에서도 opera와 같은 문제가 있음.
|
||||
*/
|
||||
if(this._loadFunc){ req.removeEventListener("load", this._loadFunc, false); }
|
||||
this._loadFunc = function(rq){
|
||||
clearTimeout(_timer);
|
||||
_timer = undefined;
|
||||
t._onload(rq);
|
||||
}
|
||||
req.addEventListener("load", this._loadFunc, false);
|
||||
}else{
|
||||
if (typeof req.onload != "undefined") {
|
||||
req.onload = function(rq){
|
||||
if(req.readyState == 4 && !t._is_abort){
|
||||
clearTimeout(_timer);
|
||||
_timer = undefined;
|
||||
t._onload(rq);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
/*
|
||||
* IE6에서는 onreadystatechange가 동기적으로 실행되어 timeout이벤트가 발생안됨.
|
||||
* 그래서 interval로 체크하여 timeout이벤트가 정상적으로 발생되도록 수정. 비동기 방식일때만
|
||||
|
||||
*/
|
||||
if(window.navigator.userAgent.match(/(?:MSIE) ([0-9.]+)/)[1]==6&&opt.async){
|
||||
var onreadystatechange = function(rq){
|
||||
if(req.readyState == 4 && !t._is_abort){
|
||||
if(_timer){
|
||||
clearTimeout(_timer);
|
||||
_timer = undefined;
|
||||
}
|
||||
t._onload(rq);
|
||||
clearInterval(t._interval);
|
||||
t._interval = undefined;
|
||||
}
|
||||
};
|
||||
this._interval = setInterval(onreadystatechange,300);
|
||||
|
||||
}else{
|
||||
req.onreadystatechange = function(rq){
|
||||
if(req.readyState == 4){
|
||||
clearTimeout(_timer);
|
||||
_timer = undefined;
|
||||
t._onload(rq);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req.send(data);
|
||||
return this;
|
||||
};
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>FileUploader Callback</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
// alert("callback");
|
||||
// document.domain 설정
|
||||
try { document.domain = "http://localhost"; } catch(e) {}
|
||||
|
||||
// execute callback script
|
||||
var sUrl = document.location.search.substr(1);
|
||||
if (sUrl != "blank") {
|
||||
var oParameter = {}; // query array
|
||||
|
||||
sUrl.replace(/([^=]+)=([^&]*)(&|$)/g, function(){
|
||||
oParameter[arguments[1]] = arguments[2];
|
||||
return "";
|
||||
});
|
||||
|
||||
if ((oParameter.errstr || '').length) { // on error
|
||||
(parent.jindo.FileUploader._oCallback[oParameter.callback_func+'_error'])(oParameter);
|
||||
} else {
|
||||
(parent.jindo.FileUploader._oCallback[oParameter.callback_func+'_success'])(oParameter);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
<%--------------------------------------------------------------------------------
|
||||
* 화면명 : Smart Editor 2.8 에디터 - 싱글 파일 업로드 처리
|
||||
* 파일명 : /SE2/sample/photo_uploader/file_uploader.jsp
|
||||
--------------------------------------------------------------------------------%>
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ page import="java.util.List"%>
|
||||
<%@ page import="java.util.UUID"%>
|
||||
<%@ page import="java.io.File"%>
|
||||
<%@ page import="java.io.FileOutputStream"%>
|
||||
<%@ page import="java.io.InputStream"%>
|
||||
<%@ page import="java.io.OutputStream"%>
|
||||
<%@ page import="org.apache.commons.fileupload.FileItem"%>
|
||||
<%@ page import="org.apache.commons.fileupload.disk.DiskFileItemFactory"%>
|
||||
<%@ page import="org.apache.commons.fileupload.servlet.ServletFileUpload"%>
|
||||
<%
|
||||
// 로컬경로에 파일 저장하기 ============================================
|
||||
String return1 = "";
|
||||
String return2 = "";
|
||||
String return3 = "";
|
||||
String name = "";
|
||||
|
||||
// multipart로 전송되었는가 체크
|
||||
if(ServletFileUpload.isMultipartContent(request)) {
|
||||
ServletFileUpload uploadHandler = new ServletFileUpload(new DiskFileItemFactory());
|
||||
|
||||
// UTF-8 인코딩 설정
|
||||
uploadHandler.setHeaderEncoding("UTF-8");
|
||||
|
||||
List<FileItem> items = uploadHandler.parseRequest(request);
|
||||
|
||||
// 각 필드태그들을 FOR문을 이용하여 비교를 합니다.
|
||||
for(FileItem item : items) {
|
||||
if(item.getFieldName().equals("callback")) {
|
||||
return1 = item.getString("UTF-8");
|
||||
} else if(item.getFieldName().equals("callback_func")) {
|
||||
return2 = "?callback_func="+item.getString("UTF-8");
|
||||
} else if(item.getFieldName().equals("Filedata")) {
|
||||
// FILE 태그가 1개이상일 경우
|
||||
if(item.getSize() > 0) {
|
||||
// 확장자
|
||||
String ext = item.getName().substring(item.getName().lastIndexOf(".")+1);
|
||||
|
||||
// 파일 기본경로
|
||||
String defaultPath = request.getServletContext().getRealPath("/");
|
||||
|
||||
// 파일 기본경로 _ 상세경로
|
||||
String path = defaultPath + "upload" + File.separator;
|
||||
|
||||
File file = new File(path);
|
||||
|
||||
// 디렉토리 존재하지 않을경우 디렉토리 생성
|
||||
if(!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
|
||||
// 서버에 업로드 할 파일명(한글문제로 인해 원본파일은 올리지 않는것이 좋음)
|
||||
String realname = UUID.randomUUID().toString() + "." + ext;
|
||||
|
||||
///////////////// 서버에 파일쓰기 /////////////////
|
||||
InputStream is = item.getInputStream();
|
||||
OutputStream os=new FileOutputStream(path + realname);
|
||||
int numRead;
|
||||
byte b[] = new byte[(int)item.getSize()];
|
||||
|
||||
while((numRead = is.read(b,0,b.length)) != -1) {
|
||||
os.write(b,0,numRead);
|
||||
}
|
||||
|
||||
if(is != null) is.close();
|
||||
|
||||
os.flush();
|
||||
os.close();
|
||||
|
||||
System.out.println("path : "+path);
|
||||
System.out.println("realname : "+realname);
|
||||
|
||||
// 파일 삭제
|
||||
// File f1 = new File(path, realname);
|
||||
// if (!f1.isDirectory()) {
|
||||
// if(!f1.delete()) {
|
||||
// System.out.println("File 삭제 오류!");
|
||||
// }
|
||||
// }
|
||||
|
||||
///////////////// 서버에 파일쓰기 /////////////////
|
||||
return3 += "&bNewLine=true&sFileName="+name+"&sFileURL=/upload/"+realname;
|
||||
} else {
|
||||
return3 += "&errstr=error";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.sendRedirect(return1+return2+return3);
|
||||
// ./로컬경로에 파일 저장하기 ============================================
|
||||
%>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
// default redirection
|
||||
$url = $_REQUEST["callback"].'?callback_func='.$_REQUEST["callback_func"];
|
||||
$bSuccessUpload = is_uploaded_file($_FILES['Filedata']['tmp_name']);
|
||||
|
||||
// SUCCESSFUL
|
||||
if(bSuccessUpload) {
|
||||
$tmp_name = $_FILES['Filedata']['tmp_name'];
|
||||
$name = $_FILES['Filedata']['name'];
|
||||
|
||||
$filename_ext = strtolower(array_pop(explode('.',$name)));
|
||||
$allow_file = array("jpg", "png", "bmp", "gif");
|
||||
|
||||
if(!in_array($filename_ext, $allow_file)) {
|
||||
$url .= '&errstr='.$name;
|
||||
} else {
|
||||
$uploadDir = '../../upload/';
|
||||
if(!is_dir($uploadDir)){
|
||||
mkdir($uploadDir, 0777);
|
||||
}
|
||||
|
||||
$newPath = $uploadDir.urlencode($_FILES['Filedata']['name']);
|
||||
|
||||
@move_uploaded_file($tmp_name, $newPath);
|
||||
|
||||
$url .= "&bNewLine=true";
|
||||
$url .= "&sFileName=".urlencode(urlencode($name));
|
||||
$url .= "&sFileURL=upload/".urlencode(urlencode($name));
|
||||
}
|
||||
}
|
||||
// FAILED
|
||||
else {
|
||||
$url .= '&errstr=error';
|
||||
}
|
||||
|
||||
header('Location: '. $url);
|
||||
?>
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<%--------------------------------------------------------------------------------
|
||||
* 화면명 : Smart Editor 2.8 에디터 - 다중 파일 업로드 처리
|
||||
* 파일명 : /SE2/sample/photo_uploader/file_uploader_html5.jsp
|
||||
--------------------------------------------------------------------------------%>
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ page import="java.util.List"%>
|
||||
<%@ page import="java.util.UUID"%>
|
||||
<%@ page import="java.io.File"%>
|
||||
<%@ page import="java.io.FileOutputStream"%>
|
||||
<%@ page import="java.io.InputStream"%>
|
||||
<%@ page import="java.io.OutputStream"%>
|
||||
<%@ page import="org.apache.commons.fileupload.FileItem"%>
|
||||
<%@ page import="org.apache.commons.fileupload.disk.DiskFileItemFactory"%>
|
||||
<%@ page import="org.apache.commons.fileupload.servlet.ServletFileUpload"%>
|
||||
<%
|
||||
// 로컬경로에 파일 저장하기 ============================================
|
||||
String sFileInfo = "";
|
||||
|
||||
// 파일명 - 싱글파일업로드와 다르게 멀티파일업로드는 HEADER로 넘어옴
|
||||
String name = request.getHeader("file-name");
|
||||
|
||||
// 확장자
|
||||
String ext = name.substring(name.lastIndexOf(".")+1);
|
||||
|
||||
// 파일 기본경로
|
||||
String defaultPath = request.getServletContext().getRealPath("/");
|
||||
|
||||
// 파일 기본경로 _ 상세경로
|
||||
String path = defaultPath + "upload" + File.separator;
|
||||
|
||||
File file = new File(path);
|
||||
if(!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
|
||||
String realname = UUID.randomUUID().toString() + "." + ext;
|
||||
InputStream is = request.getInputStream();
|
||||
OutputStream os = new FileOutputStream(path + realname);
|
||||
int numRead;
|
||||
|
||||
// 파일쓰기
|
||||
byte b[] = new byte[Integer.parseInt(request.getHeader("file-size"))];
|
||||
while((numRead = is.read(b,0,b.length)) != -1) {
|
||||
os.write(b,0,numRead);
|
||||
}
|
||||
|
||||
if(is != null) {
|
||||
is.close();
|
||||
}
|
||||
|
||||
os.flush();
|
||||
os.close();
|
||||
|
||||
System.out.println("path : "+path);
|
||||
System.out.println("realname : "+realname);
|
||||
|
||||
// 파일 삭제
|
||||
// File f1 = new File(path, realname);
|
||||
// if (!f1.isDirectory()) {
|
||||
// if(!f1.delete()) {
|
||||
// System.out.println("File 삭제 오류!");
|
||||
// }
|
||||
// }
|
||||
|
||||
sFileInfo += "&bNewLine=true&sFileName="+ name+"&sFileURL="+"/upload/"+realname;
|
||||
out.println(sFileInfo);
|
||||
|
||||
// ./로컬경로에 파일 저장하기 ============================================
|
||||
%>
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
$sFileInfo = '';
|
||||
$headers = array();
|
||||
|
||||
foreach($_SERVER as $k => $v) {
|
||||
if(substr($k, 0, 9) == "HTTP_FILE") {
|
||||
$k = substr(strtolower($k), 5);
|
||||
$headers[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$file = new stdClass;
|
||||
$file->name = str_replace("\0", "", rawurldecode($headers['file_name']));
|
||||
$file->size = $headers['file_size'];
|
||||
$file->content = file_get_contents("php://input");
|
||||
|
||||
$filename_ext = strtolower(array_pop(explode('.',$file->name)));
|
||||
$allow_file = array("jpg", "png", "bmp", "gif");
|
||||
|
||||
if(!in_array($filename_ext, $allow_file)) {
|
||||
echo "NOTALLOW_".$file->name;
|
||||
} else {
|
||||
$uploadDir = '../../upload/';
|
||||
if(!is_dir($uploadDir)){
|
||||
mkdir($uploadDir, 0777);
|
||||
}
|
||||
|
||||
$newPath = $uploadDir.iconv("utf-8", "cp949", $file->name);
|
||||
|
||||
if(file_put_contents($newPath, $file->content)) {
|
||||
$sFileInfo .= "&bNewLine=true";
|
||||
$sFileInfo .= "&sFileName=".$file->name;
|
||||
$sFileInfo .= "&sFileURL=upload/".$file->name;
|
||||
}
|
||||
|
||||
echo $sFileInfo;
|
||||
}
|
||||
?>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 718 B |
|
After Width: | Height: | Size: 553 B |
|
After Width: | Height: | Size: 553 B |
|
After Width: | Height: | Size: 157 B |
|
After Width: | Height: | Size: 506 B |
|
|
@ -0,0 +1,390 @@
|
|||
/**
|
||||
* Jindo Component
|
||||
* @version 1.0.3
|
||||
* NHN_Library:Jindo_Component-1.0.3;JavaScript Components for Jindo;
|
||||
* @include Component, UIComponent, FileUploader
|
||||
*/
|
||||
jindo.Component = jindo.$Class({
|
||||
_htEventHandler: null,
|
||||
_htOption: null,
|
||||
$init: function () {
|
||||
var aInstance = this.constructor.getInstance();
|
||||
aInstance.push(this);
|
||||
this._htEventHandler = {};
|
||||
this._htOption = {};
|
||||
this._htOption._htSetter = {};
|
||||
},
|
||||
option: function (sName, vValue) {
|
||||
switch (typeof sName) {
|
||||
case "undefined":
|
||||
return this._htOption;
|
||||
case "string":
|
||||
if (typeof vValue != "undefined") {
|
||||
if (sName == "htCustomEventHandler") {
|
||||
if (typeof this._htOption[sName] == "undefined") {
|
||||
this.attach(vValue);
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
this._htOption[sName] = vValue;
|
||||
if (typeof this._htOption._htSetter[sName] == "function") {
|
||||
this._htOption._htSetter[sName](vValue);
|
||||
}
|
||||
} else {
|
||||
return this._htOption[sName];
|
||||
}
|
||||
break;
|
||||
case "object":
|
||||
for (var sKey in sName) {
|
||||
if (sKey == "htCustomEventHandler") {
|
||||
if (typeof this._htOption[sKey] == "undefined") {
|
||||
this.attach(sName[sKey]);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this._htOption[sKey] = sName[sKey];
|
||||
if (typeof this._htOption._htSetter[sKey] == "function") {
|
||||
this._htOption._htSetter[sKey](sName[sKey]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
optionSetter: function (sName, fSetter) {
|
||||
switch (typeof sName) {
|
||||
case "undefined":
|
||||
return this._htOption._htSetter;
|
||||
case "string":
|
||||
if (typeof fSetter != "undefined") {
|
||||
this._htOption._htSetter[sName] = jindo.$Fn(fSetter, this).bind();
|
||||
} else {
|
||||
return this._htOption._htSetter[sName];
|
||||
}
|
||||
break;
|
||||
case "object":
|
||||
for (var sKey in sName) {
|
||||
this._htOption._htSetter[sKey] = jindo.$Fn(sName[sKey], this).bind();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
fireEvent: function (sEvent, oEvent) {
|
||||
oEvent = oEvent || {};
|
||||
var fInlineHandler = this['on' + sEvent],
|
||||
aHandlerList = this._htEventHandler[sEvent] || [],
|
||||
bHasInlineHandler = typeof fInlineHandler == "function",
|
||||
bHasHandlerList = aHandlerList.length > 0;
|
||||
if (!bHasInlineHandler && !bHasHandlerList) {
|
||||
return true;
|
||||
}
|
||||
aHandlerList = aHandlerList.concat();
|
||||
oEvent.sType = sEvent;
|
||||
if (typeof oEvent._aExtend == 'undefined') {
|
||||
oEvent._aExtend = [];
|
||||
oEvent.stop = function () {
|
||||
if (oEvent._aExtend.length > 0) {
|
||||
oEvent._aExtend[oEvent._aExtend.length - 1].bCanceled = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
oEvent._aExtend.push({
|
||||
sType: sEvent,
|
||||
bCanceled: false
|
||||
});
|
||||
var aArg = [oEvent],
|
||||
i, nLen;
|
||||
for (i = 2, nLen = arguments.length; i < nLen; i++) {
|
||||
aArg.push(arguments[i]);
|
||||
}
|
||||
if (bHasInlineHandler) {
|
||||
fInlineHandler.apply(this, aArg);
|
||||
}
|
||||
if (bHasHandlerList) {
|
||||
var fHandler;
|
||||
for (i = 0, fHandler;
|
||||
(fHandler = aHandlerList[i]); i++) {
|
||||
fHandler.apply(this, aArg);
|
||||
}
|
||||
}
|
||||
return !oEvent._aExtend.pop().bCanceled;
|
||||
},
|
||||
attach: function (sEvent, fHandlerToAttach) {
|
||||
if (arguments.length == 1) {
|
||||
jindo.$H(arguments[0]).forEach(jindo.$Fn(function (fHandler, sEvent) {
|
||||
this.attach(sEvent, fHandler);
|
||||
}, this).bind());
|
||||
return this;
|
||||
}
|
||||
var aHandler = this._htEventHandler[sEvent];
|
||||
if (typeof aHandler == 'undefined') {
|
||||
aHandler = this._htEventHandler[sEvent] = [];
|
||||
}
|
||||
aHandler.push(fHandlerToAttach);
|
||||
return this;
|
||||
},
|
||||
detach: function (sEvent, fHandlerToDetach) {
|
||||
if (arguments.length == 1) {
|
||||
jindo.$H(arguments[0]).forEach(jindo.$Fn(function (fHandler, sEvent) {
|
||||
this.detach(sEvent, fHandler);
|
||||
}, this).bind());
|
||||
return this;
|
||||
}
|
||||
var aHandler = this._htEventHandler[sEvent];
|
||||
if (aHandler) {
|
||||
for (var i = 0, fHandler;
|
||||
(fHandler = aHandler[i]); i++) {
|
||||
if (fHandler === fHandlerToDetach) {
|
||||
aHandler = aHandler.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
detachAll: function (sEvent) {
|
||||
var aHandler = this._htEventHandler;
|
||||
if (arguments.length) {
|
||||
if (typeof aHandler[sEvent] == 'undefined') {
|
||||
return this;
|
||||
}
|
||||
delete aHandler[sEvent];
|
||||
return this;
|
||||
}
|
||||
for (var o in aHandler) {
|
||||
delete aHandler[o];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
jindo.Component.factory = function (aObject, htOption) {
|
||||
var aReturn = [],
|
||||
oInstance;
|
||||
if (typeof htOption == "undefined") {
|
||||
htOption = {};
|
||||
}
|
||||
for (var i = 0, el;
|
||||
(el = aObject[i]); i++) {
|
||||
oInstance = new this(el, htOption);
|
||||
aReturn[aReturn.length] = oInstance;
|
||||
}
|
||||
return aReturn;
|
||||
};
|
||||
jindo.Component.getInstance = function () {
|
||||
if (typeof this._aInstance == "undefined") {
|
||||
this._aInstance = [];
|
||||
}
|
||||
return this._aInstance;
|
||||
};
|
||||
jindo.UIComponent = jindo.$Class({
|
||||
$init: function () {
|
||||
this._bIsActivating = false;
|
||||
},
|
||||
isActivating: function () {
|
||||
return this._bIsActivating;
|
||||
},
|
||||
activate: function () {
|
||||
if (this.isActivating()) {
|
||||
return this;
|
||||
}
|
||||
this._bIsActivating = true;
|
||||
if (arguments.length > 0) {
|
||||
this._onActivate.apply(this, arguments);
|
||||
} else {
|
||||
this._onActivate();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
deactivate: function () {
|
||||
if (!this.isActivating()) {
|
||||
return this;
|
||||
}
|
||||
this._bIsActivating = false;
|
||||
if (arguments.length > 0) {
|
||||
this._onDeactivate.apply(this, arguments);
|
||||
} else {
|
||||
this._onDeactivate();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}).extend(jindo.Component);
|
||||
jindo.FileUploader = jindo.$Class({
|
||||
_bIsActivating: false,
|
||||
_aHiddenInput: [],
|
||||
$init: function (elFileSelect, htOption) {
|
||||
var htDefaultOption = {
|
||||
sUrl: '',
|
||||
sCallback: '',
|
||||
htData: {},
|
||||
sFiletype: '*',
|
||||
sMsgNotAllowedExt: "업로드가 허용되지 않는 파일형식입니다",
|
||||
bAutoUpload: false,
|
||||
bAutoReset: true,
|
||||
bActivateOnload: true
|
||||
};
|
||||
this.option(htDefaultOption);
|
||||
this.option(htOption || {});
|
||||
this._el = jindo.$(elFileSelect);
|
||||
this._wel = jindo.$Element(this._el);
|
||||
this._elForm = this._el.form;
|
||||
this._aHiddenInput = [];
|
||||
this.constructor._oCallback = {};
|
||||
this._wfChange = jindo.$Fn(this._onFileSelectChange, this);
|
||||
if (this.option("bActivateOnload")) {
|
||||
this.activate();
|
||||
}
|
||||
},
|
||||
_appendIframe: function () {
|
||||
var sIframeName = 'tmpFrame_' + this._makeUniqueId();
|
||||
this._welIframe = jindo.$Element(jindo.$('<iframe name="' + sIframeName + '" src="' + this.option("sCallback") + '?blank">')).css({
|
||||
width: '10px',
|
||||
border: '2px',
|
||||
height: '10px',
|
||||
left: '10px',
|
||||
top: '10px'
|
||||
});
|
||||
document.body.appendChild(this._welIframe.$value());
|
||||
},
|
||||
_removeIframe: function () {
|
||||
this._welIframe.leave();
|
||||
},
|
||||
getBaseElement: function () {
|
||||
return this.getFileSelect();
|
||||
},
|
||||
getFileSelect: function () {
|
||||
return this._el;
|
||||
},
|
||||
getFormElement: function () {
|
||||
return this._elForm;
|
||||
},
|
||||
upload: function () {
|
||||
this._appendIframe();
|
||||
var elForm = this.getFormElement(),
|
||||
welForm = jindo.$Element(elForm),
|
||||
sIframeName = this._welIframe.attr("name"),
|
||||
sFunctionName = sIframeName + '_func',
|
||||
sAction = this.option("sUrl");
|
||||
welForm.attr({
|
||||
target: sIframeName,
|
||||
action: sAction
|
||||
});
|
||||
this._aHiddenInput.push(this._createElement('input', {
|
||||
'type': 'hidden',
|
||||
'name': 'callback',
|
||||
'value': this.option("sCallback")
|
||||
}));
|
||||
this._aHiddenInput.push(this._createElement('input', {
|
||||
'type': 'hidden',
|
||||
'name': 'callback_func',
|
||||
'value': sFunctionName
|
||||
}));
|
||||
for (var k in this.option("htData")) {
|
||||
this._aHiddenInput.push(this._createElement('input', {
|
||||
'type': 'hidden',
|
||||
'name': k,
|
||||
'value': this.option("htData")[k]
|
||||
}));
|
||||
}
|
||||
for (var i = 0; i < this._aHiddenInput.length; i++) {
|
||||
elForm.appendChild(this._aHiddenInput[i]);
|
||||
}
|
||||
this.constructor._oCallback[sFunctionName + '_success'] = jindo.$Fn(function (oParameter) {
|
||||
this.fireEvent("success", {
|
||||
htResult: oParameter
|
||||
});
|
||||
delete this.constructor._oCallback[oParameter.callback_func + '_success'];
|
||||
delete this.constructor._oCallback[oParameter.callback_func + '_error'];
|
||||
for (var i = 0; i < this._aHiddenInput.length; i++) {
|
||||
jindo.$Element(this._aHiddenInput[i]).leave();
|
||||
}
|
||||
this._aHiddenInput.length = 0;
|
||||
this._removeIframe();
|
||||
}, this).bind();
|
||||
this.constructor._oCallback[sFunctionName + '_error'] = jindo.$Fn(function (oParameter) {
|
||||
this.fireEvent("error", {
|
||||
htResult: oParameter
|
||||
});
|
||||
delete this.constructor._oCallback[oParameter.callback_func + '_success'];
|
||||
delete this.constructor._oCallback[oParameter.callback_func + '_error'];
|
||||
for (var i = 0; i < this._aHiddenInput.length; i++) {
|
||||
jindo.$Element(this._aHiddenInput[i]).leave();
|
||||
}
|
||||
this._aHiddenInput.length = 0;
|
||||
this._removeIframe();
|
||||
}, this).bind();
|
||||
|
||||
elForm.submit();
|
||||
if (this.option("bAutoReset")) {
|
||||
this.reset();
|
||||
}
|
||||
},
|
||||
reset: function () {
|
||||
var elWrapForm = jindo.$("<form>");
|
||||
this._wel.wrap(elWrapForm);
|
||||
elWrapForm.reset();
|
||||
jindo.$Element(elWrapForm).replace(this._el);
|
||||
var elForm = this.getFormElement(),
|
||||
welForm = jindo.$Element(elForm);
|
||||
welForm.attr({
|
||||
target: this._sPrevTarget,
|
||||
action: this._sAction
|
||||
});
|
||||
return this;
|
||||
},
|
||||
_onActivate: function () {
|
||||
var elForm = this.getFormElement(),
|
||||
welForm = jindo.$Element(elForm);
|
||||
this._sPrevTarget = welForm.attr("target");
|
||||
this._sAction = welForm.attr("action");
|
||||
this._el.value = "";
|
||||
this._wfChange.attach(this._el, "change");
|
||||
},
|
||||
_onDeactivate: function () {
|
||||
this._wfChange.detach(this._el, "change");
|
||||
},
|
||||
_makeUniqueId: function () {
|
||||
return new Date().getMilliseconds() + Math.floor(Math.random() * 100000);
|
||||
},
|
||||
_createElement: function (name, attributes) {
|
||||
var el = jindo.$("<" + name + ">");
|
||||
var wel = jindo.$Element(el);
|
||||
for (var k in attributes) {
|
||||
wel.attr(k, attributes[k]);
|
||||
}
|
||||
return el;
|
||||
},
|
||||
_checkExtension: function (sFile) {
|
||||
var aType = this.option("sFiletype").split(';');
|
||||
for (var i = 0, sType; i < aType.length; i++) {
|
||||
sType = (aType[i] == "*.*") ? "*" : aType[i];
|
||||
sType = sType.replace(/^\s+|\s+$/, '');
|
||||
sType = sType.replace(/\./g, '\\.');
|
||||
sType = sType.replace(/\*/g, '[^\\\/]+');
|
||||
if ((new RegExp(sType + '$', 'gi')).test(sFile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
_onFileSelectChange: function (we) {
|
||||
var sValue = we.element.value,
|
||||
bAllowed = this._checkExtension(sValue),
|
||||
htParam = {
|
||||
sValue: sValue,
|
||||
bAllowed: bAllowed,
|
||||
sMsgNotAllowedExt: this.option("sMsgNotAllowedExt")
|
||||
};
|
||||
if (sValue.length && this.fireEvent("select", htParam)) {
|
||||
if (bAllowed) {
|
||||
if (this.option("bAutoUpload")) {
|
||||
this.upload();
|
||||
}
|
||||
} else {
|
||||
alert(htParam.sMsgNotAllowedExt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).extend(jindo.UIComponent);
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="Content-Script-Type" content="text/javascript">
|
||||
<meta http-equiv="Content-Style-Type" content="text/css">
|
||||
<title>사진 첨부하기 :: SmartEditor2</title>
|
||||
<style type="text/css">
|
||||
/* NHN Web Standard 1Team JJS 120106 */
|
||||
/* Common */
|
||||
body,p,h1,h2,h3,h4,h5,h6,ul,ol,li,dl,dt,dd,table,th,td,form,fieldset,legend,input,textarea,button,select{margin:0;padding:0}
|
||||
body,input,textarea,select,button,table{font-family:'돋움',Dotum,Helvetica,sans-serif;font-size:12px}
|
||||
img,fieldset{border:0}
|
||||
ul,ol{list-style:none}
|
||||
em,address{font-style:normal}
|
||||
a{text-decoration:none}
|
||||
a:hover,a:active,a:focus{text-decoration:underline}
|
||||
|
||||
/* Contents */
|
||||
.blind{visibility:hidden;position:absolute;line-height:0}
|
||||
#pop_wrap{width:383px}
|
||||
#pop_header{height:26px;padding:14px 0 0 20px;border-bottom:1px solid #ededeb;background:#f4f4f3}
|
||||
.pop_container{padding:11px 20px 0}
|
||||
#pop_footer{margin:21px 20px 0;padding:10px 0 16px;border-top:1px solid #e5e5e5;text-align:center}
|
||||
h1{color:#333;font-size:14px;letter-spacing:-1px}
|
||||
.btn_area{word-spacing:2px}
|
||||
.pop_container .drag_area{overflow:hidden;overflow-y:auto;position:relative;width:341px;height:129px;margin-top:4px;border:1px solid #eceff2}
|
||||
.pop_container .drag_area .bg{display:block;position:absolute;top:0;left:0;width:341px;height:129px;background:#fdfdfd url(./img/bg_drag_image.png) 0 0 no-repeat}
|
||||
.pop_container .nobg{background:none}
|
||||
.pop_container .bar{color:#e0e0e0}
|
||||
.pop_container .lst_type li{overflow:hidden;position:relative;padding:7px 0 6px 8px;border-bottom:1px solid #f4f4f4;vertical-align:top}
|
||||
.pop_container :root .lst_type li{padding:6px 0 5px 8px}
|
||||
.pop_container .lst_type li span{float:left;color:#222}
|
||||
.pop_container .lst_type li em{float:right;margin-top:1px;padding-right:22px;color:#a1a1a1;font-size:11px}
|
||||
.pop_container .lst_type li a{position:absolute;top:6px;right:5px}
|
||||
.pop_container .dsc{margin-top:6px;color:#666;line-height:18px}
|
||||
.pop_container .dsc_v1{margin-top:12px}
|
||||
.pop_container .dsc em{color:#13b72a}
|
||||
.pop_container2{padding:46px 60px 20px}
|
||||
.pop_container2 .dsc{margin-top:6px;color:#666;line-height:18px}
|
||||
.pop_container2 .dsc strong{color:#13b72a}
|
||||
.upload{margin:0 4px 0 0;_margin:0;padding:6px 0 4px 6px;border:solid 1px #d5d5d5;color:#a1a1a1;font-size:12px;border-right-color:#efefef;border-bottom-color:#efefef;length:300px;}
|
||||
:root .upload{padding:6px 0 2px 6px;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="pop_wrap">
|
||||
<!-- header -->
|
||||
<div id="pop_header">
|
||||
<h1>사진 첨부하기</h1>
|
||||
</div>
|
||||
<!-- //header -->
|
||||
<!-- container -->
|
||||
|
||||
<!-- [D] HTML5인 경우 pop_container 클래스와 하위 HTML 적용
|
||||
그밖의 경우 pop_container2 클래스와 하위 HTML 적용 -->
|
||||
<div id="pop_container2" class="pop_container2">
|
||||
<!-- content -->
|
||||
<!-- <form id="editor_upimage" name="editor_upimage" action="FileUploader.php" method="post" enctype="multipart/form-data" onSubmit="return false;"> -->
|
||||
<form id="editor_upimage" name="editor_upimage" method="post" enctype="multipart/form-data" onSubmit="return false;">
|
||||
<div id="pop_content2">
|
||||
<input type="file" class="upload" id="uploadInputBox" name="Filedata">
|
||||
<p class="dsc" id="info"><strong>10MB</strong>이하의 이미지 파일만 등록할 수 있습니다.<br>(JPG, GIF, PNG, BMP)</p>
|
||||
</div>
|
||||
</form>
|
||||
<!-- //content -->
|
||||
</div>
|
||||
<div id="pop_container" class="pop_container" style="display:none;">
|
||||
<!-- content -->
|
||||
<div id="pop_content">
|
||||
<p class="dsc"><em id="imageCountTxt">0장</em>/10장 <span class="bar">|</span> <em id="totalSizeTxt">0MB</em>/50MB</p>
|
||||
<!-- [D] 첨부 이미지 여부에 따른 Class 변화
|
||||
첨부 이미지가 있는 경우 : em에 "bg" 클래스 적용 //첨부 이미지가 없는 경우 : em에 "nobg" 클래스 적용 -->
|
||||
|
||||
<div class="drag_area" id="drag_area">
|
||||
<ul class="lst_type" >
|
||||
</ul>
|
||||
<em class="blind">마우스로 드래그해서 이미지를 추가해주세요.</em><span id="guide_text" class="bg"></span>
|
||||
</div>
|
||||
<div style="display:none;" id="divImageList"></div>
|
||||
<p class="dsc dsc_v1"><em>한 장당 10MB, 1회에 50MB까지, 10개</em>의 이미지 파일을<br>등록할 수 있습니다. (JPG, GIF, PNG, BMP)</p>
|
||||
</div>
|
||||
<!-- //content -->
|
||||
</div>
|
||||
|
||||
<!-- //container -->
|
||||
<!-- footer -->
|
||||
<div id="pop_footer">
|
||||
<div class="btn_area">
|
||||
<a href="#"><img src="./img/btn_confirm.png" width="49" height="28" alt="확인" id="btn_confirm"></a>
|
||||
<a href="#"><img src="./img/btn_cancel.png" width="48" height="28" alt="취소" id="btn_cancel"></a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- //footer -->
|
||||
</div>
|
||||
<script type="text/javascript" src="jindo.min.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="jindo.fileuploader.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="attach_photo.js" charset="utf-8"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Smart Editor™ WYSIWYG Mode</title>
|
||||
</head>
|
||||
<body class="se2_inputarea" style="height:0;-webkit-nbsp-mode:normal"></body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">
|
||||
<title>Smart Editor™ WYSIWYG Mode</title>
|
||||
</head>
|
||||
<body class="se2_inputarea" style="height:0"></body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
|
||||
http://www.springframework.org/schema/util
|
||||
http://www.springframework.org/schema/util/spring-util-4.0.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context-4.0.xsd
|
||||
http://www.springframework.org/schema/task
|
||||
http://www.springframework.org/schema/task/spring-task-3.0.xsd
|
||||
">
|
||||
|
||||
<context:component-scan base-package="com.pms"></context:component-scan>
|
||||
|
||||
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
|
||||
<property name="basenames">
|
||||
<list>
|
||||
<value>classpath:com/pms/message/message</value>
|
||||
<value>classpath:com/pms/message/common</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="cacheSeconds">
|
||||
<value>60</value>
|
||||
</property>
|
||||
<property name="defaultEncoding" value="UTF-8"/>
|
||||
</bean>
|
||||
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
|
||||
<property name="defaultLocale" value="ko"/>
|
||||
</bean>
|
||||
|
||||
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
|
||||
<property name="paramName" value="lang" />
|
||||
</bean>
|
||||
|
||||
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
|
||||
<property name="prefix" value="/WEB-INF/view"/>
|
||||
<property name="suffix" value=".jsp"/>
|
||||
<property name="order" value="1"/>
|
||||
</bean>
|
||||
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
|
||||
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
|
||||
<property name="messageConverters">
|
||||
<list>
|
||||
<ref bean="jacksonMessageConverter"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
<task:scheduler id="gsScheduler" pool-size="10" />
|
||||
<task:annotation-driven scheduler="gsScheduler" />
|
||||
</beans>
|
||||
|
||||